Ural(Timus) 1003 Parity
并查集
题意:题意比较好懂简单说一下。一个序列,只有0,1;输入n,表示序列长度(从1到n标号),输入m,下面m个更新,每行都是a,b,string,表示说序列中下标a到下标b的元素中有偶数个或奇数个1.没得到一个更新就更新序列的信息,知道读入第k个信息,和已建立的信息矛盾,那么结束,输出k-1,表示前面k-1个更新不矛盾,如果m个更新都成立,那么输出m
这题要转化一下,一转化就比较明显了。我们定义前缀和为sum[i]表示1到i的和,那么sum[b]-sum[a-1]=c[a]+c[a+1]+c[a+2]……c[b] , 即序列的[a,b]区间和
因为序列中只有0,1所以区间和的奇偶性就是该区间拥有1的奇偶数,即[a,b]有偶数个1的话[a,b]区间和为偶数,同时也可知sum[b]和sum[a-1]同偶或同奇,即奇偶性相同
如果[a,b]中有奇数个1,那么sum[b]和sum[a-1]奇偶性不同
所以这个转化关系就出来了:[a,b]有偶数个1,那么sum[a-1]和sum[b]的奇偶性相同 ; [a,b]有奇数个1,那么sum[a-1]和sum[b]的奇偶性不同
而无论如何都好,sum[i]的奇偶性只能是两种,或奇或偶,所以其实sum[0],sum[1],sum[2],……sum[n]其实分成了两个阵营,也就是两个集合
如果读入了[a,b]为偶数,sum[a-1]和sum[b],应该在同一个集合中。如果它们互相在对方的敌对集合中,那么矛盾,跳出,否则的话,记得合并它们为1个集合,并且他们的敌对集合也要合并为1个集合
如果读入[a,b]为奇数,sum[a-1]和sum[b],应该不在同一个集合中并且应该在对方的敌对集合中。如果它们在同一个集合中,那么矛盾,跳出,否则的话,sum[a-1]合并到sum[b]的敌对集合中,sum[b]合并到sum[i-1]的敌对集合中
所以这题其实和经典在“朋友敌人”题目是相同(ab是朋友bc是朋友则ac是朋友,两人又共同敌人则是朋友,其实所有人分成了两个阵营)
所以这种题目都要想方法怎么建立敌人集合
一个比较好的方法是设置“虚拟”的敌人,已知有n个人,那么虚拟设置另外n个人(从n+1到2*n)标号,i+n表示i的敌人,其实i+n并不存在(只有n个人),它只是用了作为一个标志,标杆。这个方法有个好处,就是最初的时候我们什么信息都不知道,并不知道每个元素的敌人和朋友是谁,换言之我们不知道敌人集合是哪个(朋友集合可以初始化为它自己,即一开始自身就是个集合),所以我们用i+n作为i的敌人集合,然后在以后的更新中不断更新即可
这不是唯一的方法但我认为是一个不容易出错,代码少,好理解的方法
最后差点忘记了,这题数据太大了,10^9,而更新的次数只有5000,也就是说最坏情况下降产生10000个点,所以我们要离散化,而且可以发现序列的长度n,其实根本就没有用到,不需要它
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 10010 //最多10000个点 #define M 5010 #define min(a,b) a<b?a:b #define max(a,b) a>b?a:b int m,np; struct ran{ int l,r,v; }a[M]; struct point{ int n,m; }c[N]; int p[2*N]; int cmp(struct point a ,struct point b) { return a.n<b.n; } int init() //输入数据和离散化 { scanf("%d",&m); np=0; for(int i=0; i<m; i++) { int l,r; char s[5]; scanf("%d%d%s",&l,&r,s); a[i].v=(s[0]=='o'); c[np].m=i; c[np].n=l; c[np+1].m=i; c[np+1].n=r; np+=2; } sort(c,c+np,cmp); int mm,nn=0; for(int i=0; i<np; i++) { if(i==0 || c[i].n != c[i-1].n) ++nn; mm=c[i].m; a[mm].r=a[mm].l; a[mm].l=nn; } // for(int i=0; i<m; i++) printf("[%d,%d] %d\n",a[i].l,a[i].r,a[i].v); //离散化结束 return nn; } int find(int x) { return x==p[x] ? x : p[x]=find(p[x]); } void solve(int n) { for(int i=0; i<=2*N+1; i++) p[i]=i; int res=m; for(int i=0; i<m; i++) { int l=a[i].l , r=a[i].r , v=a[i].v; int t=max(l,r) , tt=min(l,r); l=tt-1; r=t; int fal,far,eml,emr; fal=find(l); far=find(r); eml=find(l+n+1); emr=find(r+n+1); if(!v) { if(fal == emr && far == eml) { res=i; break;} p[fal]=far; p[eml]=emr; } else { if(fal == far) { res=i; break; } p[fal]=emr; p[far]=eml; } } printf("%d\n",res); } int main() { int n; while(scanf("%d",&n)!=EOF && n!=-1) { n=init(); solve(n); } return 0; }