【并查集】P1197 [JSOI2008] 星球大战
1 int find(int x) 2 { 3 if(fa[x]==x) return x; 4 return fa[x]=find(fa[x]); 5 }
路径压缩,每个经过find()后的点都直接连在根节点上,即做完后fa[x]等于find(x)
1 int fi=find(i),fj=find(vec[i][j]); 2 if(!v[vec[i][j]]&&fi!=fj) 3 fa[fj]=fi;
合并,即把一点的根指向另一点的根,fa[find(x)]=find(y);
vector储存邻接表
P1197 [JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
注意题意 求出每一次打击之后反抗军占据的星球的连通块的个数,每次打击的星球不再属于反抗军。因此样例中最后虽然没有相通的,但块数为3不是8
关键点:逆向思维,变打击为修复,逆向向内加点,计算每次连通的块数。邻接点尚未加入无效,已在同一块中无效,并查集一共合并了多少次,连通块就减少几个。
加入一个点,还未合并前块数++
每次计算的结果是前一次打击过后的答案。
(我开始的做法 每次整个扫一遍看有几个根,On2结果t飞6个点。。)
1 #include<cstdio> 2 #include<vector> 3 using namespace std; 4 const int maxn=4e5+10; 5 int fa[maxn]; 6 int v[maxn],qu[maxn],ans[maxn]; 7 vector<int> vec[maxn]; 8 int find(int x) 9 { 10 if(fa[x]==x) return x; 11 return fa[x]=find(fa[x]); 12 } 13 int main() 14 { 15 int n,m,k; 16 scanf("%d%d",&n,&m); 17 for(int i=0;i<n;i++) fa[i]=i; 18 for(int i=0;i<m;i++) 19 { 20 int x,y; 21 scanf("%d%d",&x,&y); 22 vec[x].push_back(y); 23 vec[y].push_back(x); 24 } 25 scanf("%d",&k); 26 for(int i=0;i<k;i++) 27 { 28 scanf("%d",&qu[i]); 29 v[qu[i]]=1; 30 } 31 ans[k]=n-k; 32 for(int i=0;i<n;i++) 33 { 34 if(!v[i]) 35 for(int j=0;j<vec[i].size();j++) 36 { 37 int fi=find(i),fj=find(vec[i][j]); 38 if(!v[vec[i][j]]&&fi!=fj) { 39 fa[fj]=fi; 40 ans[k]--; 41 } 42 } 43 }; 44 for(int ii=k-1;ii>=0;ii--) 45 { 46 int i=qu[ii];v[i]=0;ans[ii]=ans[ii+1]+1; 47 for(int j=0;j<vec[i].size();j++) 48 { 49 int fi=find(i),fj=find(vec[i][j]); 50 if(!v[vec[i][j]]&&fi!=fj) { 51 fa[fj]=fi; 52 ans[ii]--; 53 } 54 } 55 } 56 for(int i=0;i<=k;i++) 57 printf("%d\n",ans[i]); 58 return 0; 59 }
浙公网安备 33010602011771号