Codeforces Round #668 (Div. 1)

A

很容易可以想到 s [ i ] = = s [ i + k ] s[i] == s[i+k] 所以每个位置 s [ i ] s[i] 都应该等于 s [ i % k ] s[i \% k] ,然后再判断前 k k 个如果是确定的 0 0 或者 1 1 ,有没有超过 k / 2 k/2 就可以了

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(3e5) + 10;
const int mod = 1e9+7;
const int inf = 1e9 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

char s[N];
int fa[N],siz[N]; int find(int x){return (fa[x] == x) ? fa[x] : fa[x] = find(fa[x]);}

signed main() {
  
  // freopen("a.in","r",stdin);
  // freopen("a.out","w",stdout);

  int t = rd();
  while(t--) {
    int n = rd(); int m = rd();
    scanf("%s",s+1);
    for(int i=1;i<=n+2;i++) fa[i] = i;
    for(int i=1;i<=n;i++) {
      if(s[i] == '0') {
        int x = find(i); int y = find(n+1);
        if(x!=y) fa[x] = y;
      }else if(s[i] == '1') {
        int x = find(i); int y = find(n+2);
        if(x!=y) fa[x] = y;
      }
      if(i + m <= n) {
        int x = find(i); int y = find(i+m);
        if(x!=y) fa[x] = y;
      }
      if(i - m > 0) {
        int x = find(i); int y = find(i-m);
        if(x!=y) fa[x] = y;
      }
    }

    if(find(n+1) == find(n+2)) puts("NO");
    else {
      bool bk = 1;
      int s0 = 0; int s1 = 0;
      for(int i=1;i<=n;i++) {
        if(find(i) == find(n+1)) s0++;
        if(find(i) == find(n+2)) s1++;
        if(i>m) {
          if(find(i-m) == find(n+1)) s0--;
          if(find(i-m) == find(n+2)) s1--;
        }
        if(s0 > m/2 || s1 > m/2){bk = 0; break;}
      }
      if(bk) puts("YES");
      else puts("NO");
    }
  }


  return 0;
}

B

首先如果要Bob赢可以想到三个结论,假设树的直径为 D D
2 d a < D 2da <D 不然的话Alice先跳到树的重心就一定能抓到Bob
2 d a < d b 2da < db 这个是因为如果Alice和Bob的距离是 d a \leq da ,Bob必须要跳到距离Alice > d a >da 的地方
满足这两个条件,如果当前是Bob跳的话,Bob是一定能跳到距离Alice > d a >da 的地方,因为Bob肯定可以跳到直径上距离Alice比较远的地方(这个需要脑补一下证明)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(1e5) + 10;
const int mod = 1e9+7;
const int inf = 1e9 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

struct node{int x,y,nex;}edge[N<<1]; int len,fir[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].nex = fir[x]; fir[x] = len;}
int n,a,b,da,db;
int dep[N];
void dfs(int x,int f) {
  for(int k=fir[x];k!=-1;k=edge[k].nex) {
    int y = edge[k].y;
    if(y==f) continue;
    dep[y] = dep[x] + 1; dfs(y,x);
  }
}

signed main(){
  int t = rd();
  while(t--) {
    n = rd(); a = rd(); b = rd(); da = rd(); db = rd();
    len = 0; for(int i=1;i<=n;i++) fir[i] = -1;
    for(int i=1;i<n;i++) {
      int x = rd(); int y = rd();
      ins(x,y); ins(y,x);
    }
    dep[a] = 0; dfs(a,0);
    int p=0; for(int i=1;i<=n;i++) if(dep[i] > dep[p]) p = i;
    if(dep[b] <= da){puts("Alice"); continue;}
    dep[p] = 0; dfs(p,0);
    p=0; for(int i=1;i<=n;i++) if(dep[i] > dep[p]) p = i;
    if(dep[p] > 2 * da && db > 2*da){puts("Bob"); continue;}
    else{puts("Alice"); continue;} 
  }
  
}

C

定义每一个位置,它如果可以被消掉,肯定要满足 i a [ i ] > = 0 i-a[i] >= 0 并且前面要消除掉 i a [ i ] i-a[i]
我们会发现每一个位置能不能消除,在于前面消除掉多少个,而前面消除掉多少个,与询问的 L L 有关
我们不妨设 m x [ i ] mx[i] 为如果第 i i 个位置被消除掉,询问时的L出现的最大值。
对于一个 m x [ i ] mx[i] 肯定是前面所有 m x [ i ] mx[i] 中最大的 i a [ i ] i-a[i] 个消掉转移过来的,所以可以用一个线段树来维护
对于区间询问 [ l , r ] [l,r] ,我们只需要问区间内有多少个 m x [ i ] > = l mx[i] >= l 就好了

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(3e5) + 10;
const int mod = 1e9+7;
const int inf = 1e9 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

int rt[N],tot,lc[N*20],rc[N*20],c[N*20];
void link(int &u,int L,int R,int k,int val)
{
  if(!u) u=++tot;
  if(L==R){c[u] += val; return ;}
  int mid = (L+R)>>1;
  if(k<=mid) link(lc[u],L,mid,k,val);
  else link(rc[u],mid+1,R,k,val);
  c[u] = c[lc[u]] + c[rc[u]];
}
int qry(int u,int L,int R,int k)
{
  if(c[u] < k) return -inf;
  if(L==R) return L;
  int mid = (L+R)>>1;
  if(c[rc[u]] >= k) return qry(rc[u],mid+1,R,k);
  else return qry(lc[u],L,mid,k-c[rc[u]]);
}

void merge(int &u1,int u2) {
  if(!u2) return ;
  if(!u1){u1 = u2; return ;}
  c[u1] += c[u2];
  merge(lc[u1],lc[u2]);
  merge(rc[u1],rc[u2]);
}

int qry2(int u1,int u2,int L,int R,int l,int r)
{
  if(L==l && R==r) return c[u2] - c[u1];
  int mid = (L+R)>>1;
  if(r<=mid) return qry2(lc[u1],lc[u2],L,mid,l,r);
  else if(l>mid) return qry2(rc[u1],rc[u2],mid+1,R,l,r);
  else return qry2(lc[u1],lc[u2],L,mid,l,mid) + qry2(rc[u1],rc[u2],mid+1,R,mid+1,r);
}

int a[N]; int mx[N];



signed main() {
  
  // freopen("a.in","r",stdin);
  // freopen("a.out","w",stdout);

  int n = rd(); int q = rd();
  for(int i=1;i<=n;i++) a[i] = rd();
  for(int i=1;i<=n;i++) a[i] = i- a[i];

  rt[0] = tot = 0;
  for(int i=1;i<=n;i++) if(a[i] >= 0) {
    if(a[i] == 0) mx[i] = i;
    else {
      mx[i] = qry(rt[0],1,n,a[i]);
    }
    if(mx[i] != -inf) link(rt[0],1,n,mx[i],1);
    // printf("%lld : %lld\n",i,mx[i]);
  }else mx[i] = -inf;

  for(int i=1;i<=tot;i++) lc[i] = rc[i] = c[i] = 0;
  rt[0] = tot = 0;
  for(int i=1;i<=n;i++) {
    if(mx[i] != -inf) link(rt[i],1,n,mx[i],1);
    merge(rt[i],rt[i-1]);
  }

  while(q--) {
    int l = rd(); int r = rd(); l = l+1; r = n-r;
    // printf("%lld %lld\n",l,r);
    printf("%lld\n",qry2(rt[l-1],rt[r],1,n,l,r));
  }

  return 0;
}

D

考虑什么时候可以是 F i r s t First

如果N是偶数的话,我们把 ( i , n + i ) (i,n+i) 放在一起,这样的话,假设 N = 2 M N=2M ,有:
0 + 1 + . . . + ( N 1 ) N ( N 1 ) 2 M ( 2 M 1 ) M ( m o d N ) 0+1+...+(N-1) \equiv \frac{N(N-1)}{2} \equiv M(2M-1) \equiv M \pmod{N}
( m o d N ) \pmod N 下是不为 0 0 的,所以在 ( m o d 2 N ) \pmod{2N} 下也是不为 0 0

考虑N是奇数的情况:
0 + 1 + . . . + ( N 1 ) N ( N 1 ) 2 0 N ( m o d 2 N ) 0+1+...+(N-1) \equiv \frac{N(N-1)}{2} \equiv 0或者N \pmod{2N}

这时我们把数分成两组, ( 1 , 2 , . . . , 2 N ) (1,2,...,2*N) ( N , N + 1 , . . . , 2 N 1 ) (N,N+1,...,2*N-1) ,每一组在 ( m o d N ) \pmod N 下都是 0 , 1 , . . . , n 1 0,1,...,n-1 ,一组是加上奇数个 N N ,一组是加上偶数个 N N
我们同样是把 ( m o d N ) \pmod N 剩余 0 , 1 , . . . , n 1 0,1,...,n-1 的数给选一遍,这样的话,选出来的两组数,在选中加上奇数个 N N 的那一组数的个数来看,肯定一组是有奇数个,一组是有偶数个,所以我们根据 m o d mod 完后的情况再去调整就好了,这样就证明出来 S e c o n d Second 的情况。

具体的做法就是把输入中同一组的,和 ( i , i + n ) (i,i+n) 连边,然后黑白染色,这样就会出现很多个环,然后要不取黑色,要不取白色就好了。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(1e6) + 10;
const int mod = 1e9+7;
const int inf = 1e9 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

int n; vector<int> g[N],ans[2]; 
int px[N]; bool v[N];

void dfs(int x,int op) {
  v[x] = 1; ans[op].PB(x);
  for(auto u:g[x]) if(!v[u]) dfs(u,op^1);
}

signed main(){
  cin >> n;
  if(n % 2 == 0) {
    cout << "First" << endl;
    for(int i=1;i<=2*n;i++) cout << ((i <= n) ? i : (i-n)) << " ";
    cout << endl;
    int op; cin >> op;
    assert(op == 0);
  }
  else {
    cout << "Second" << endl;
    for(int i=1;i<=2*n;i++) {
      int x; cin >> x;
      if(!px[x]) px[x] = i;
      else g[i].PB(px[x]),g[px[x]].PB(i);
    }
    for(int i=1;i<=n;i++) g[i].PB(i+n),g[i+n].PB(i);
    for(int i=1;i<=2*n;i++) if(!v[i]) dfs(i,0);
    int s = 0;
    for(int i=0;i<ans[0].size();i++) s+=ans[0][i];
    if(s % (2*n) != 0) swap(ans[0],ans[1]);
    for(int i=0;i<ans[0].size();i++) cout << ans[0][i] << " ";
    cout << endl;
    int op; cin >> op;
    assert(op == 0);  
  }
}

E

在这里插入图片描述
把相邻两个 # \# 的边界变成点,把相邻的横着的边和竖着的边对应的点连线,跑一个最大独立集,跑出来的点集就是可以减少掉的边界。因为不能出现 L L 形这种覆盖,所以要这样连边。
这样的二分图匹配跑一次网络流就好了。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
#define int long long
using namespace std;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(2e2) + 10;
const int mod = 1e9+7;
const int inf = 1e9 + 10;
const long double pi = acos(-1);
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

struct node{int x,y,nex,c,other;}edge[N*N*30]; int len,fir[N*N*4];
void ins(int x,int y,int c) {
  len++; int k1 = len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].nex = fir[x]; fir[x] = len;
  len++; int k2 = len; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].nex = fir[y]; fir[y] = len;
  edge[k1].other=k2; edge[k2].other = k1;
}
queue<int> q; int dep[N*N*4]; int s,d;
bool bfs() {
  while(!q.empty()) q.pop();
  memset(dep,0,sizeof(dep));
  q.push(s); dep[s] = 1;
  while(!q.empty()) {
    int x = q.front();
    for(int k=fir[x];k!=-1;k=edge[k].nex) {
      int y = edge[k].y;
      if(dep[y] == 0 && edge[k].c) {
        dep[y] = dep[x] + 1;
        q.push(y);
      }
    }q.pop();
  }return dep[d] > 0;
}
int dfs(int x,int flow) {
  if(x == d) return flow;
  int delta = 0;
  for(int k=fir[x];k!=-1;k=edge[k].nex) {
    int y = edge[k].y;
    if(dep[y] == dep[x] + 1 && edge[k].c && flow > delta) {
      int minf = dfs(y,min(flow-delta,edge[k].c));
      edge[k].c -= minf; edge[edge[k].other].c += minf; delta += minf;
    }
  }
  if(delta == 0) dep[x] = 0;
  return delta;
}

int MaxFlow() {
  int ans = 0;
  while(bfs()) {
    ans += dfs(s,inf);
  }return ans;
}

int n,m; char str[N][N]; int idL[N][N],idR[N][N],tot;

signed main(){

  // freopen("a.in","r",stdin);
  n = rd(); m = rd(); int ans = 0;
  for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
    scanf("\n%c",&str[i][j]);
    if(str[i][j] == '#') ans ++;
  }
  len = 0; memset(fir,-1,sizeof(fir));
  s = 1; d = 2; tot = 2;
  for(int i=1;i<n;i++) for(int j=1;j<=m;j++) if(str[i][j] == '#' && str[i+1][j] == '#') idL[i][j] = ++tot,ins(s,idL[i][j],1);
  for(int i=1;i<=n;i++) for(int j=1;j<m;j++) if(str[i][j] == '#' && str[i][j+1] == '#') idR[i][j] = ++tot,ins(idR[i][j],d,1);
  for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) {
    if(idL[i][j] && idR[i][j]) ins(idL[i][j],idR[i][j],1);
    if(idL[i-1][j] && idR[i][j]) ins(idL[i-1][j],idR[i][j],1);
    if(idL[i][j] && idR[i][j-1]) ins(idL[i][j],idR[i][j-1],1);
    if(idL[i][j] && idR[i+1][j-1]) ins(idL[i][j],idR[i+1][j-1],1);
  }

  // for(int i=1;i<=len;i++) printf("%lld %lld %lld\n",edge[i].x,edge[i].y,edge[i].c);
  // printf("%lld %lld\n",((tot-2) >> 1) , MaxFlow());
  return printf("%lld\n",ans - ((tot-2) - MaxFlow())),0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39708759/article/details/108448371