2020牛客暑期多校训练营(第八场)A.All-Star Game

思路:
由题可以得到一个结论:fans和player组成的联通快只需要一个player就可以让这个联通快内所有fans都满足条件。
那就直接把每个关系当成一个边,按时间建线段树,用可撤销并查集,维护图的连通性即可。
对于有单独的fans组成一个联通快的显然无解了。。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include 
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<
int n,m,q;
vector<pair<int,int>>v[N<<2];
#define mid (l+r>>1)
#define ls o<<1
#define rs o<<1|1
void ins(int o,int l,int r,int x,int y,int dx,int dy){
  if(l>=x&&r<=y){
    v[o].emplace_back(dx,dy);
    return;
  }
  if(x<=mid)ins(ls,l,mid,x,y,dx,dy);
  if(y>mid)ins(rs,mid+1,r,x,y,dx,dy);
}
set<pair<int,int> >g[N];
int tot=0;
int st[N],f[N],sz[N],cn[N],co[N];
int find(int x){
  while(f[x]!=x){
    x=f[x];
  }
  return x;
}
int now;
int sum,tim;
//一个联通快 只需要一个player...
int need[N];
void merge(int x,int y){
  int dx=find(x),dy=find(y);
  if(dx==dy)
    return;
  if(sz[dx]>sz[dy])swap(dx,dy);
  if(sz[dx]==1&&dx<=n)need[dx]=0,tim--;
  if(sz[dy]==1&&dy<=n)need[dy]=0,tim--;
  sz[dy]+=sz[dx];
  if(co[dx]&&co[dy]){
    now--;
  }

  co[dy]+=co[dx];
  f[dx]=dy;
  st[++tot]=dx;
  return;
}
void go_pre(int dx){
  while(tot>dx){
    int x=st[tot];
    sz[f[x]]-=sz[x];
    if(co[f[x]]-co[x]&&co[x]){
      now++;
    }
    co[f[x]]-=co[x];
    if(sz[f[x]]==1&&f[x]<=n)need[f[x]]=1,tim++;
    if(sz[x]==1&&x<=n)need[x]=1,tim++;
    f[x]=x;
    tot--;
  }
}
int an[N];
void get(int o,int l,int r){
  for(auto k:v[o]){
    merge(k.fi,k.se+n);
//    cout<
  }
  if(l==r){
    if(tim)an[l]=-1;
    else an[l]=now;
    return;
  }
  int last=tot;
  get(ls,l,mid);
  //cout<<"pre->"<"<
  go_pre(last);
  get(rs,mid+1,r);
  go_pre(last);
}
int main() {
  ios::sync_with_stdio(false);
  cin>>m>>n>>q;now=n;tim=n;
  for(int i=1;i<=n;i++) {
    f[i] = i;
    need[i]=1;
    sz[i]=1;
    co[i]=1;
  }
  for(int i=n+1;i<=n+m;i++){
    f[i]=i;
    sz[i]=1;
  }
  for(int i=1;i<=m;i++){
    int k;
    cin>>k;
    for(int j=1;j<=k;j++){
      int x;
      cin>>x;
      g[x].insert({i,1});
    }
  }
  for(int i=1;i<=q;i++){
    int x,y;
    cin>>x>>y;
    auto z=g[x].lower_bound({y,0});
    if(z!=g[x].end()){
      if((*z).fi==y){
        ins(1,1,q+1,(*z).se,i,x,(*z).fi);
//        cout<<(*z).fi<<' '<<(*z).se<
        g[x].erase(*z);
      }else{
        g[x].insert({y,i+1});
      }
    }else{
      g[x].insert({y,i+1});
    }
  }
  for(int i=1;i<=n;i++){
    for(auto k:g[i]){
      ins(1,1,q+1,k.se,q+1,i,k.fi);
    }
  }
  get(1,1,q+1);
  for(int i=2;i<=q+1;i++)cout<<an[i]<<'\n';
  return 0;
}

你可能感兴趣的:(线段树,并查集)