UVALive - 6910 (离线逆序并查集)
题意:给处编号从1~n这n个节点的父节点,得到含有若干棵树的森林;然后再给出k个操作,分两种'C x'是将节点x与其父节点所连接的支剪短;'Q a b'是询问a和b是否在同一棵树中。
题解:一开始拿到题目绞尽脑汁咋都想不到哇该怎么做在线查询,,,除了用暴力外一脸懵逼(不过确实能用暴力水过...=_=); 正解就是将其逆序离线处理: 先根据k个查询去掉所有要剪去的边后将剩余的边进行并查集处理,然后将k个查询逆序地处理查询结果;如果是剪边操作,则是将该节点与父节点并起来。 另外要注意的是,同一条边可能剪多次。
1 /** 2 *@author Wixson 3 */ 4 #include <iostream> 5 #include <algorithm> 6 #include <cstdio> 7 #include <cstring> 8 #include <cmath> 9 #include <set> 10 #include <utility> 11 #include <vector> 12 #include <map> 13 #include <queue> 14 #include <stack> 15 const int inf=0x3f3f3f3f; 16 const double PI=acos(-1.0); 17 const double EPS=1e-10; 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int,int> P; 21 22 int n,k; 23 int a[20005],f[20005]; 24 int ans[5050]; 25 int book[20005]; 26 typedef struct node 27 { 28 char str[5]; 29 int x,y; 30 } node; 31 node q[5050]; 32 void init() 33 { 34 for(int i=1; i<=n; i++) f[i]=i; 35 } 36 int find(int x) 37 { 38 if(f[x]==x) return x; 39 return f[x]=find(f[x]); 40 } 41 void unite(int x,int y) 42 { 43 int tx=find(x),ty=find(y); 44 if(tx!=ty) f[ty]=tx; 45 } 46 int main() 47 { 48 //freopen("input.txt","r",stdin); 49 int t; 50 scanf("%d",&t); 51 for(int K=1; K<=t; K++) 52 { 53 scanf("%d%d",&n,&k); 54 for(int i=1; i<=n; i++) scanf("%d",&a[i]); 55 // 56 memset(book,0,sizeof(book)); 57 for(int i=1; i<=k; i++) 58 { 59 scanf("%s",q[i].str); 60 if(q[i].str[0]=='Q') scanf("%d%d",&q[i].x,&q[i].y); 61 else 62 { 63 scanf("%d",&q[i].x); 64 if(a[q[i].x]) book[q[i].x]++; 65 } 66 } 67 // 68 init(); 69 for(int i=1; i<=n; i++) 70 { 71 if(!a[i]||book[i]) continue; 72 // 73 unite(a[i],i); 74 } 75 // 76 int cnt=1; 77 for(int i=k; i>=1; i--) 78 { 79 if(q[i].str[0]=='Q') 80 { 81 if(find(q[i].x)==find(q[i].y)) ans[cnt++]=1; 82 else ans[cnt++]=0; 83 } 84 else 85 { 86 if(a[q[i].x]) 87 { 88 book[q[i].x]--; 89 if(!book[q[i].x]) unite(a[q[i].x],q[i].x); 90 } 91 } 92 } 93 // 94 printf("Case #%d:\n",K); 95 for(int i=cnt-1; i>=1; i--) if(ans[i]) printf("YES\n"); 96 else printf("NO\n"); 97 } 98 return 0; 99 }