Parity game——带权并查集

题目链接

题意:

你一个字符串,由0和1组成,并且告诉你子串里面1的个数,假设前面的话都是对的,问你到哪一句和前面的话矛盾。

题解:

首先,发现n很大,但是问题数m不多,所以先离散化

d数组表示序列S的前缀和
d[l~r]有偶数个1,等价于d[l-1]与d[r]奇偶性相同。

d[l~r]有奇数个1,等价于d[l-1]与d[r]奇偶性不同。

然后通过异或满足上面传递关系 即 奇偶性相同异或为偶  奇偶性不同异或为奇

集合合并方法与 How Many Answers Are Wrong 类似

d【i】 表示 1到i的和 如果存在 【i,a】与【i,b】 则只要判断 【b,i】^【a-1,i】的奇偶性即可

所以用并查集维护公共点 即根节点  (其实d【i】可以看作根节点到 i 的距离)

那么集合如何合并?

先让fx指向fy 即 f[fy]=fx;    因为 d[fx]^d[x]^d[y]=a[i].ans  所以  d[fx]^d[x]^d[y]=a[i].ans

代码:

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
vector<int>v;
int f[maxn],d[maxn];
int get_id(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
struct node
{
    int l,r,ans;
} a[maxn];
int Find(int x)
{
    if(f[x]==x)return x;
    int root=Find(f[x]);
    d[x]^=d[f[x]];
    return f[x]=root;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
    {
        char str[10];
        scanf("%d%d%s",&a[i].l,&a[i].r,str);
        v.push_back(a[i].l-1);
        v.push_back(a[i].r);
        a[i].ans=(str[0]=='e'?0:1);
    }
    sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1; i<=v.size(); i++)f[i]=i,d[i]=0;
    int res=m;
    for(int i=1; i<=m; i++)
    {
        int x=get_id(a[i].l-1);
        int y=get_id(a[i].r);
        int fx=Find(x);
        int fy=Find(y);
        if(fx==fy)
        {
            if((d[x]^d[y])!=a[i].ans)
            {
                res=i-1;
                break;
            }
        }
        else
        {
            f[fx]=fy;
            d[fx]^=d[x]^d[y]^a[i].ans; ///  d[fx]^d[x]^d[y]=a[i].ans
        }
    }
    printf("%d\n",res);


    return 0;
}
View Code

 

猜你喜欢

转载自www.cnblogs.com/j666/p/11615302.html