P1197 [JSOI2008]星球大战 题解
作者:互联网
这题有着很深的背景呢……
本孱弱第一次写题解,请多多支持
这题最有意义的地方是:逆序思考。
不断的维护摧毁后的数量,会非常困难。但是!~
我们不妨去试着重建,一点一点重建。假设这些都是已经被摧毁了的,那么我们如果修复该怎么办呢?我们可以把它周围的所有道路都重新连起来,并且通过并查集合并。话不多说,上代码。
#include<bits/stdc++.h>
using namespace std;
#define N 400005
#define M 400005 //一定要开双倍啊
int k;
int v[N];//记录每一次的情况
int ans[N];//记录每一次的答案
bool dan[N];//当前有没有被损坏
struct mp{//个人喜好:所有东西写在结构体里
int n,m,cnt;
int last[N],Before[M],ver[M],tot;//链式前向星套装
int fa[N];
int find(int x){//并查集标准操作
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void init(){
cnt=n;
for(int i=0;i<n;i++){
last[i]=-1;
fa[i]=i;
}
tot=-1;
}
void add(int x,int y){//前向星
ver[++tot]=y;
Before[tot]=last[x];
last[x]=tot;
ver[++tot]=x;
Before[tot]=last[y];
last[y]=tot;
}
void father(int s){//判断重建后的点可以更新的路径
int a=find(s);
int b;
for(int i=last[s];i!=-1;i=Before[i]){
int y=ver[i];
if(dan[y])continue;
b=find(y);
if(fa[b]!=a){
fa[b]=a;
cnt--;
//cout<<s<<"->"<<y<<endl;
find(y);
}
}
}
}G;
int main(){
scanf("%d%d",&G.n,&G.m);
G.init();
int x,y;
for(int i=1;i<=G.m;i++){
scanf("%d%d",&x,&y);
G.add(x,y);
}
scanf("%d",&k);
//printf("%d\n",G.cnt);
G.cnt-=k;
for(int i=1;i<=k;i++){
scanf("%d",&v[i]);
dan[v[i]]=true;
}
for(int i=0;i<G.n;i++){
if(!dan[i]){//cout<<1<<endl;
G.father(i);
}
}//在第一次操作后,我们需要把此时的情况(也就是最后的情况)记录下来
ans[k]=G.cnt;
for(int i=k;i>=1;i--){
dan[v[i]]=false;
G.cnt++;//不要忘记:修复好的点自身也是一个联通块
G.father(v[i]);
ans[i-1]=G.cnt;
}
for(int i=0;i<=k;i++){
printf("%d\n",ans[i]);
}
return 0;
}
/*
10 10
0 1
0 4
2 0
4 5
2 3
1 3
6 7
7 9
6 9
5 0
4
0
3
6
9
孱弱数据,观赏即可
*/
标签:cnt,return,int,题解,JSOI2008,fa,find,P1197 来源: https://blog.csdn.net/ljrcpp2zmm/article/details/120443108