2020牛客暑期多校训练第二场部分

2020牛客多校训练第二场

出题数 2 --- D题(真水题) 和 F题(滑动窗口)

D、Duration

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
int main()
{
  int ah , am , as , bh , bm , bs ;
  scanf("%d:%d:%d" , &ah , &am , &as) ;
  scanf("%d:%d:%d" , &bh , &bm , &bs) ;
  ll a = ah * 3600 + am * 60 + as ;
  ll b = bh * 3600 + bm * 60 + bs ;
  cout << abs(a - b) << endl ;
  return 0 ;
}
/*
*/

F Fake Maxpooling

在这里插入图片描述
整个矩阵很好求,然后要求求每个k * k矩阵的最大值,我先预处理出来每个k * 1的最大值,然后按照上图滑动窗口的时候,左面丢出一个k * 1, 右面多出一个k * 1, 直接按照滑动窗口的模式就可以,然后这是一行的, 然后枚举每行, 做滑动窗口就行了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
int a[5010][5010] , ans = 0 , maxn[5010][5010] ;
int q[5010] ;
int main()
{
  int n = in() , m = in() , k = in() ;
  for(int i = 0; i < n ;i ++ ) {
    for(int j = 0; j < m ;j ++ ) {
      a[i][j] = 1ll * (i + 1) * (j + 1) / __gcd(i+ 1 ,1 +  j) ;
    }
  }
 
  for(int j = 0; j < m ;j ++ ) {
    int hh = 0 , tt = -1 ;
    for(int i = 0; i < n ;i ++ ) {
      if(i - k + 1 > q[hh]) ++ hh ;
      while(hh <= tt && a[i ][j] >= a[q[tt]][j]) -- tt ;
      q[++ tt] = i  ;
      if(i + 1 >= k) maxn[i - k + 1][j] = a[q[hh]][j] ;
    }
 
  }
   ll ans = 0 ;
   for(int i = 0 ;i < n ;i ++ ) {
     int hh = 0 , tt = -1 ;
     for(int j = 0 ;j < m ;j ++ ) {
       if(j - k + 1 > q[hh]) ++ hh ;
       while(hh <= tt && maxn[i][j] >= maxn[i][q[tt]]) -- tt ;
       q[++ tt] = j ;
       if(j + 1 >= k) {
         ans += 1ll * maxn[i][q[hh]]  ;
       }
     }
   }
   cout << ans << endl ;
  return 0 ;
}
/*
*/

补题系列

C Cover the Tree

一条链 , 两个端点, 并且这两个端点都是度为1的点 , 要求最小的数量,就是(n + 1) / 2, 如果n是偶数的话, 就两两配对,如果是奇数的话,再多一条链。那么怎么配对呢。这个看的大佬的代码是这样写的。
![](https://img-blog.csdnimg.cn/20200713185331709.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Nwbm9veXNlZWQ=,size_16,color_FFFFFF,t_70
边(u , v) , u < v, 最终的图也就是上图 , 就直接小标号的叶子节点匹配大标号的叶子节点

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
int deg[N] , res[N];
int main()
{
  int n = in() ;
  for(int i = 1 , a ,b ;i < n ;i ++ ) {
    a = in() , b = in() ;
    deg[a] ++ , deg[b] ++ ;
  }
  int ans = 0 ;
  for(int i = 1; i <= n ;i ++ )
   if(deg[i] == 1)
    res[++ ans] = i ;
  cout << (ans + 1) / 2 << endl ;
  for(int i = 1; i <= (ans + 1) / 2;i ++ )
   cout << res[i] << " " << res[ans / 2 + i] << endl ;
  return 0 ;
}
/*
*/

J.Just Shuffle

此题相当于一个单位置换群e,通过置换 P置换群 k次得到一个置换群A

\[e * P^k = A\\ e*P = A^{k^{-1}}\\P = A^{k^{-1}} \]

然后将i和a[i]连接起来,就会得到一些环,每个环就是一个置换群,也就是每个A,然后求A的逆元就行了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
int vis[N] , a[N] , b[N] , res[N] ;
int main()
{
  int n = in() , k = in() ;
  for(int i = 1; i <= n ;i ++ ) a[i] = in() ;
  for(int i = 1; i <= n ;i ++ ) {
    if(vis[i]) continue ;
    int pos = i ;
    vector<int> ans ;
    while(!vis[pos]) ans.push_back(pos) , vis[pos] = 1 , pos = a[pos] ;
    int size = ans.size() ;
    int t = 0 ;
    while(t < size) {
      if(1ll * t * k % size == 1) break ;
      t ++ ;
    }
    for(int i = 0 ;i < size ;i ++ )
     res[ans[i]] = ans[(i + t) % size] ;
  }
  for(int i = 1; i <= n ;i ++ ) cout << res[i] << " " ;
  puts("") ;
  return 0 ;
}
/*
*/

B、Boundary

解法一:算圆心坐标

枚举两点 , 加上原点,总共三点,三点定圆

\[(x - x[i]) ^ 2 + (y - y[i]) ^ 2 = x ^ 2 + y ^ 2 \]

\[(x - x[j]) ^ 2 + (y - y[j]) ^ 2 = x ^ 2 + y ^ 2 \]

\[x = \frac{b[j] * (x[i] ^ 2 + y[i] ^ 2) - b[i] * (x[j] ^ 2 + y[j] ^ 2)}{2x[i] * y[j] - 2 * x[j] * y[i]} \]

\[y = \frac{x[j] * (x[i] ^ 2 + y[i] ^ 2) - x[i] * (x[j] ^ 2 + y[j] ^ 2)}{2x[j] * y[i] - 2 * x[i] * y[j]} \]

先判断一下分母是否为0 , 记得用long long

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-7 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
ll x[N] , y[N] ;
double get(int i){
  return 1ll * x[i] * x[i] + y[i] * y[i] ;
}
vector<pair<double , double>> ans ;
int main()
{
  int n = in() ;
  for(int i = 1; i <= n ;i ++ ) x[i] = in() , y[i] = in() ;
  int res = 0 ;
  for(int i = 1; i <= n ;i ++ ) {
    ans.clear() ;
    for(int j = 1 ; j <= n ;j ++ ) {
      ll  yy = x[j] * get(i) - x[i] * get(j) ;
      ll t = 2 * x[j] * y[i] - 2 * x[i] * y[j] ;
        if(t == 0) continue ;
      ll xx = y[j] * get(i) - y[i] * get(j) ;
      ans.push_back({(double)xx / (-t) , (double) yy / t}) ;
    }
    sort(ans.begin() , ans.end()) ;
    int cnt = 1 , maxn = 0 ;
    for(int i = 1 ; i < ans.size() ;i ++ )
     {
       if(fabs(ans[i].x - ans[i - 1].x) < esp && fabs(ans[i].y - ans[i - 1].y) < esp) cnt ++;
       else cnt = 1 ;
       maxn = max(maxn , cnt) ;
     }
    res = max(res , maxn + 1) ;
  }
  cout << res << endl ;
  return 0 ;
}
/*
*/

解法二:算夹角

在这里插入图片描述
这两个角一定相同,同时为了避免重复,在上图中二选一,也就是选择一个判断标准,op向量*oa向量 < 0, 也可以选择大于0 , 或者选择ap * ao, 然后算夹角acos(dot(ap , ao) / (len(ap) * len(ao))) , 最后随便统计一下, 不过精度好象要调到1e-9, 在1e-8都是不能完全通过的(也可能写的程序不太对)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-7, pi = acos(-1) ;
typedef pair<double , double> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
PII a[N] ;
double dot(PII a , PII b){
  return a.x * b.x + a.y * b.y ;
}
double cross(PII a , PII b){
  return a.x * b.y - a.y * b.x ;
}
double c[N] ;
double len(PII a){
  return sqrt(a.x * a.x + a.y * a.y) ;
}
double calc(PII a , PII b){
  double lena = len(a) , lenb = len(b) ;
  return acos(dot(a , b) / (lena * lenb)) ;
}
int main()
{
  int n = in() ;
  for(int i = 1; i <= n ;i ++ ) cin >> a[i].x >> a[i].y ;
  int ans = 0 ;
  for(int i = 1; i <= n ;i ++ ) {
    int cnt = 0 ;
    for(int j = 1; j <= n ;j ++ ) {
      PII op = a[i] , oa = a[j] ;
      PII ap = {a[j].x - a[i].x , a[j].y - a[i].y} , ao = {-a[j].x ,- a[j].y} ;
      if(cross(op , oa) < 0) c[++ cnt] = calc(ap , ao) ;
    }
    sort(c + 1 , c + cnt + 1) ;
    int maxn = 0 , res = 0 ;
    double t = 0 ;
    for(int i = 1; i <= cnt ;i ++ ) {
      if(fabs(c[i] - t) < esp) res ++ ;
      else res = 1 , t = c[i] ;
      maxn = max(maxn , res) ;
    }
    ans = max(ans , maxn) ;
  }
  cout << ans + 1 << endl ;
  return 0 ;
}
/*
*/

A.All with Pairs

看到这题的第一个想法就是先预处理出来每个字符串的所有后缀,用map存起来,然后再枚举每个字符串的前缀,判断有多少个之前map存储的hash值个数,但是发现题目要求是要求最大,而不是求所有的,上面的这个做法可以将所有符合前缀等于后缀的贡献求出来, 但不能求最大的。然后发现一个小小的性质,kmp算法next数组的经典应用
在这里插入图片描述
上图两个字符串匹配中蓝色竖杠之间的是前缀等于后缀的部分,但是是要求最大的,我们接着往下面匹配
在这里插入图片描述
突然后面这个绿色之间的匹配的一部分比蓝色部分更大,也就是说当前这个答案更优,那我们就不要前面那个,但是发现
在这里插入图片描述
对于黑色曲线,我们发现,1、2、3、4、5号线段字符串部分全部都是相同的,也就是1 和 5是相同的,这不就是kmp里面如果枚举到5部分突然失配了, 会跳到1部分嘛,对于这个性质应用到当前题目上面就是-----题目要求最大,我们就把所有前缀后缀相同的部分都直接算答案,不过面对上图这个情况,我们可以再kmp即将失配的时候,也就是i失配的时候,将next[i]部分的贡献减掉,即h[ne[j]] -= h[j], 这样就在把所有贡献都加上 的同时, 减掉对于当前贡献来说前面贡献比较小的部分,就是最大的贡献 , 即题目所求


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef unsigned long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 998244353 , base = 131;
ll in()
{
  ll x = 0 , f = 1 ;
  char ch = getchar() ;
  while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
  while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
  return x * f ;
}
unordered_map<ll , int> mp ;
void Hash(string s){
  ll res = 0 , p = 1 ;
  for(int i = s.size() - 1; i >= 0 ;i --) {
    res += p * s[i] ;
    p *= base ;
    mp[res] ++ ;
  }
  return ;
}
string s[N] ;
int h[N] , ne[N] ;
void get(string s){
  for(int i = 2, j = 0 ;i < s.size() ;i ++ ) {
    while(j && s[i] != s[j + 1]) j = ne[j] ;
    if(s[i] == s[j + 1]) j ++ ;
    ne[i] = j ;
  }
  return ;
}
int main()
{
  int n ;
  cin >> n ;
  for(int i = 1; i <= n ;i ++ ) {
    cin >> s[i] ;
    Hash(s[i]) ;
  }
  ll ans = 0 ;
  for(int i = 1; i <= n ;i ++ ) {
    ll res = 0 ;
    for(int j = 0 ;j < s[i].size() ;j ++ ) {
      res = res * base + s[i][j] ;
      h[j + 1] = mp[res] ;
    }

    s[i] = " " + s[i] ;
    get(s[i]) ;
    for(int j = 1 ; j < s[i].size() ;j ++ )
     h[ne[j]] -= h[j] ;

    for(int j = 1; j < s[i].size() ;j ++ )
     ans = (ans + 1ll * h[j] * j % mod * j % mod) % mod ;
  }
  cout << ans << endl ;
  return 0 ;
}
/*
*/


剩下几题咱也不会

posted @ 2020-07-17 22:20  spnooyseed  阅读(133)  评论(0编辑  收藏  举报