P1197 [JSOI2008]星球大战
考察:并查集+邻接表+逆向思维
kuangbin题单有这道题,但那道题本蒟蒻只看了思路,没有实现,今天做了一下才发现没有那么简单.....
错误思路:
离线处理,将所有的要连的边先存储,标记要摧毁的点,将还未摧毁的点先连接.再将存储的摧毁的点从尾到头遍历,边涉及本次修复的点的就连接,
结果当然是TLE
正确思路:
要存储的边用链式前向星存储,每当我们遍历一个被摧毁的点,就通过图的存储遍历到与每个与他相连的点.
关于连通块:
再摧毁k个星球后,最多剩下n-k个连通块,我们每合并两个点,都会导致连通块减少
这道题和HDU 垃圾邮件那道题不一样的地方在于,那道题删除点后,连接该点的剩余点还在连通块里,这题是否还在与路径有关.主要在于恢复点后,要快速找到与该点连接的点,并查集在这道题是维护是否在一个集合的作用.
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 using namespace std; 5 typedef pair<int,int> pii; 6 const int N = 4e5+10; 7 vector<int> v; 8 int p[N],tot,idx,h[N],ans[N]; 9 bool isknock[N]; 10 struct Road{ 11 int from,to,next; 12 }road[N]; 13 void add(int a,int b) 14 { 15 road[idx].from = a,road[idx].to = b,road[idx].next = h[a],h[a] = idx++; 16 } 17 int findf(int x) 18 { 19 if(p[x]!=x) p[x] = findf(p[x]); 20 return p[x]; 21 } 22 int main() 23 { 24 // freopen("in.txt","r",stdin); 25 int n,m,k; 26 fill(h,h+N,-1); 27 scanf("%d%d",&n,&m); 28 for(int i=1;i<=n;i++) p[i] = i; 29 for(int i=1;i<=m;i++) 30 { 31 int x,y; scanf("%d%d",&x,&y); 32 add(x,y); add(y,x); 33 } 34 scanf("%d",&k); 35 for(int i=1;i<=k;i++){ 36 int x; scanf("%d",&x); 37 v.push_back(x); 38 isknock[x]=true; 39 } 40 tot = n-k;//炸掉k个星球,至多有n-k个 连通块 41 for(int i=1;i<=m*2;i++) 42 { 43 if(!isknock[road[i-1].from]&&!isknock[road[i-1].to]) 44 { 45 int x = road[i-1].from,y = road[i-1].to; 46 int px = findf(x),py = findf(y); 47 if(px!=py) p[px] = py,tot--; 48 } 49 } 50 ans[k+1] = tot; 51 for(int j=v.size()-1;j>=0;j--) 52 { 53 tot++; 54 int x = v[j]; 55 isknock[x] = 0; 56 for(int i=h[x];i!=-1;i=road[i].next) 57 { 58 int y = road[i].to; 59 int px = findf(x),py = findf(y); 60 if(!isknock[x]&&!isknock[y]&&px!=py) 61 { 62 p[px] = py; tot --; 63 } 64 } 65 ans[j+1] = tot; 66 } 67 for(int i=1;i<=k+1;i++) printf("%d\n",ans[i]); 68 return 0; 69 }