2020牛客暑期多校训练营(第八场)A All-Star Game 题解
题意:
有 n 个球员, m 个球迷。球迷 a 会看 球员 x 的 比赛当且仅当:
1)a 是球员 x 的粉丝
2)a 和 b 都会去看 球员 y 的比赛,b 也会去看 x 的比赛。
初始有 K 组粉丝关系,一共有 q 个修改,每次修改将 a 是否是 x 的粉丝取反,每次修改之后询问如果想让所有球迷看比赛最少让多少球员打比赛,无解输出 -1 。
如果把粉丝关系看做边,很显然每一个有球迷在联通块只用出一个球员。所以我们只要判断是不是所有球迷都有球星要追以及有多少含有球迷的联通块。
其实一开始想的是按照边的消失时间来建最大生成树,但是这道题里面有中途加边的操作,最大生成树是做不了的。我们考虑可撤销并查集。
可撤销并查集并不能路径压缩,但是我们可以通过按秩合并来保证复杂度。我个人理解的就是让深度小的集合并到深度大的集合里,这样每次向上爬的复杂度可以保持最坏O(logn)。
我们还要解决的一点是如果有链接两个集合的两条边,一个在[1~5]存在,一个在[3~10]存在,如何在删除第一条边的情况下保持第二条边的状态呢?
我们就要用时间建线段树,每个节点用vector保存在这个区间所表示的时间段内存在的且不完全在他父节点所表示时间段存在的所有边。这样,在一个时间点存在的所有边的集合就是他和他的所有祖先的vector里存着的边。我们在线段树上跑一遍遍历就可以了。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<map> 8 #include<vector> 9 #define N 200005 10 using namespace std; 11 int n,m,q,zz; 12 int L[N*5]; 13 map<int,int> ma[N]; 14 struct dda{ 15 int x,y; 16 int depx,depy; 17 int ans,cnt; 18 }da[N*5],st[N*5]; 19 struct no{ 20 int left,right,mid; 21 vector<int> q1; 22 }node[N*4]; 23 void build(int left,int right,int x) 24 { 25 node[x].left=left,node[x].right=right; 26 if(left==right) 27 { 28 return; 29 } 30 int mid=(left+right)>>1; 31 node[x].mid=mid; 32 build(left,mid,x<<1); 33 build(mid+1,right,x<<1|1); 34 } 35 void update(int left,int right,int x,int da) 36 { 37 if(node[x].left==left&&node[x].right==right) 38 { 39 node[x].q1.push_back(da); 40 return; 41 } 42 int mid=node[x].mid; 43 if(left>mid) update(left,right,x<<1|1,da); 44 else if(right<=mid) update(left,right,x<<1,da); 45 else update(left,mid,x<<1,da),update(mid+1,right,x<<1|1,da); 46 } 47 int fa[N*2],deep[N*2]; 48 int find(int x) 49 { 50 if(fa[x]==x)return x; 51 return find(fa[x]); 52 } 53 int top,ans,cnt; 54 void merge(int x,int y) 55 { 56 x=find(x),y=find(y); 57 top++; 58 // cout<<x<<' '<<y<<' '<<deep[x]<<' '<<deep[y]<<endl; 59 st[top].x=x;st[top].y=y; 60 st[top].depx=deep[x],st[top].depy=deep[y]; 61 st[top].ans=ans,st[top].cnt=cnt; 62 if(x==y)return; 63 if(deep[y]==1) cnt--; 64 if(deep[x]==1&&deep[y]==1) ans++; 65 else if(deep[x]>1&&deep[y]>1) ans--; 66 if(deep[x]<deep[y])swap(x,y); 67 if(deep[x]==deep[y]) deep[x]++; 68 fa[y]=x; 69 } 70 void del() 71 { 72 fa[st[top].x]=st[top].x,fa[st[top].y]=st[top].y; 73 deep[st[top].x]=st[top].depx,deep[st[top].y]=st[top].depy; 74 ans=st[top].ans;cnt=st[top].cnt; 75 top--; 76 } 77 void work(int x) 78 { 79 int l=node[x].q1.size(); 80 int la=top; 81 for(int i=0;i<l;i++) 82 { 83 merge(da[node[x].q1[i]].x,da[node[x].q1[i]].y); 84 } 85 if(node[x].left==node[x].right) 86 { 87 // cout<<"dsasaadsaa "<<node[x].left<<' '<<node[x].right<<' '<<cnt<<endl; 88 if(cnt) printf("-1\n"); 89 else printf("%d\n",ans); 90 } 91 else 92 { 93 work(x<<1); 94 work(x<<1|1); 95 } 96 while(top!=la) 97 { 98 del(); 99 } 100 } 101 int main() 102 { 103 scanf("%d%d%d",&n,&m,&q); 104 for(int i=1;i<=n;i++) 105 { 106 int l; 107 scanf("%d",&l); 108 for(int j=1;j<=l;j++) 109 { 110 int x; 111 scanf("%d",&x); 112 zz++; 113 ma[i][x+n]=zz; 114 L[zz]=1; 115 da[zz].x=i; 116 da[zz].y=x+n; 117 } 118 } 119 build(1,q,1); 120 for(int i=1;i<=q;i++) 121 { 122 int x,y; 123 scanf("%d%d",&x,&y); 124 x+=n; 125 if(!ma[y].count(x)||!L[ma[y][x]]) 126 { 127 if(!ma[y].count(x)) 128 { 129 zz++; 130 ma[y][x]=zz; 131 L[zz]=i; 132 da[zz].x=y; 133 da[zz].y=x; 134 } 135 else 136 { 137 L[ma[y][x]]=i; 138 } 139 } 140 else 141 { 142 int tmp=ma[y][x]; 143 if(i!=1) update(L[tmp],i-1,1,tmp); 144 L[tmp]=0; 145 } 146 } 147 cnt=m; 148 for(int i=1;i<=zz;i++) 149 { 150 if(L[i]) 151 { 152 update(L[i],q,1,i); 153 } 154 } 155 for(int i=1;i<=n+m;i++) fa[i]=i,deep[i]=1; 156 work(1); 157 return 0; 158 }