题目链接:http://poj.org/problem?id=1733
题意:给定由0、1组成的数串长度n,询问次数m,每次询问给出a,b,s,表示区间[a,b]内1的数量为s(odd-奇数或even-偶数),求前几次询问的答案是正确的。
思路:此题类似与poj1182,属于并查集的向量应用。首先n<=1e9,必须要用到离散化,这里用map实现,详见代码。然后要用到带权并查集,用1表示odd,0表示even。用root[i]表示i的祖先r,f[i]表示[r,i)(左闭右开,所以输入的b要加一)之间1的个数(偶数用0表示,奇数用1表示),且(x->z)=(x->y)^(y->z)。每次查询若有相同的祖先(ra=rb),则判断是否满足(b->a)^(a->ra)==(b->rb),若不是则代表当前答案有问题,退出循环; 若祖先不同,则合并,并更新祖先rb到新祖先ra的f[rb]的值,f[rb]=(c^f[b])^f[a]。(不懂的话自己手动算一下,简单的向量运算)。
AC代码:
1 #include<cstdio> 2 #include<map> 3 using namespace std; 4 5 const int maxn=10005; 6 int n,m,cnt,root[maxn],f[maxn]; 7 map<int,int> mp; 8 9 int getr(int k){ 10 if(root[k]==k) return k; 11 else{ 12 int tmp=root[k]; 13 root[k]=getr(root[k]); 14 f[k]^=f[tmp]; 15 return root[k]; 16 } 17 } 18 19 int main(){ 20 scanf("%d%d",&n,&m); 21 for(int i=1;i<=maxn;++i) 22 root[i]=i,f[i]=0; 23 int p; 24 for(p=1;p<=m;++p){ 25 int a,b,c,ra,rb; 26 char s[5]; 27 scanf("%d%d%s",&a,&b,s); 28 if(s[0]=='e') c=0; 29 else c=1; 30 ++b; 31 if(!mp.count(a)) 32 mp[a]=++cnt; 33 if(!mp.count(b)) 34 mp[b]=++cnt; 35 a=mp[a],b=mp[b]; 36 ra=getr(a),rb=getr(b); 37 if(ra==rb){ 38 if(c^f[a]!=f[b]) 39 break; 40 } 41 else{ 42 root[rb]=ra; 43 f[rb]=(c^f[b])^f[a]; 44 } 45 } 46 printf("%d\n",p-1); 47 return 0; 48 }