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;

}

 

你可能感兴趣的:(it)