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;
}
原文地址:https://www.cnblogs.com/scau20110726/p/2998722.html