并查集——奇偶游戏(带边权和扩展域做法)

传送门:239. 奇偶游戏 - AcWing题库

思路:

第一种是用带边权的方法来解

首先对于题目中的序列,我们用一个前缀和数组sum[i]来表示1~i中所有数的奇数的个数。

可以发现假如题目给的一个范围[l,r]里面有偶数个1,等价于sum[l-1]与sum[r]的奇偶性相同,

证明:s[l,r]里面的奇偶性可以由sum[r]-sum[l-1]得到,s[l,r]有偶数个1,说明sum[r]-sum[l-1]要么是两个偶数,要么是两个奇数。否则得不出s[l,r]里面是有偶数个1这个结论。

[l,r]里面有奇数个1的情况下说明,sum[l-1]与sum[r]的奇偶性不同。证明和上面类似,此处略。

这道题目里面要传递的关系不止一种,

1.x1与x2的奇偶性相同+x2与x3的奇偶性相同可以得到x1和x3的奇偶性相同

2.x1与x2的奇偶性相同+x2与x3的奇偶性不同可以得到x1和x3的奇偶性不同

3.x1与x2的奇偶性不同+x2与x3的奇偶性不同可以得到x1和x3的奇偶性相同

步骤

0.首先注意到题目s的序列长度很大,但回答的次数却很小,所以这里要先离散化一下,

1.用一个并查集树来表示一个集合,d[i]表示当前节点跟f[x]节点的奇偶性关系,0表示相同,1表示不同。在路径压缩的同时对x到根节点路径上的所有边权做异或运算便可得到x和根节点的关系。

同时,如果想获得同一棵树里面的两个不同节点的奇偶性关系就是直接d[x]^d[y],这里就使用到了上面所说的关系传递。

2.对于每一个回答,都要先判断x,y是否属于同一集合,如果是的话就用d[x]^d[y]去对比回答里面的关系是否相同。

   如果x,y不属于同一集合,就要合并两个集合,得到两个集合的根节点pa,pb后,令pa为pb的子节     点。有ans=d[x]^d[y]^d[pa]可以得到d[pa]=ans^d[x]^d[y];

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=3e4+10;
struct nod
{
    int l,r, ans;
}node[N];
int a[N],f[N],d[N];
int n,m,t;
void getdata()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        char str[5];
        scanf("%d%d%s",&node[i].l,&node[i].r,str);
        if(str[0]=='o') node[i].ans=1;
        else node[i].ans=0;
        a[++t]=node[i].l-1;
        a[++t]=node[i].r;
    }
    sort(a+1,a+t+1);
    n=unique(a+1,a+t+1)-a-1;
}
int get(int x)
{
    if(x==f[x]) return x;
    int u=get(f[x]);
    d[x]^=d[f[x]];
    return f[x]=u;
}
int main()
{
    getdata();
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=lower_bound(a+1,a+n+1,node[i].l-1) -a;
        int y=lower_bound(a+1,a+n+1,node[i].r) -a;
        int pa=get(x),pb=get(y);
        if(pa==pb)
        {
            if((d[x]^d[y])!=node[i].ans)
            {
                cout<<i-1<<endl;
                return 0;
            }
        }else
        {
            f[pa]=pb;
            d[pa]=d[x]^d[y]^node[i].ans;
        }
    }
    cout<<m<<endl;


    return 0;
}

扩展域:

思路:

待写

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=3e4+10;
struct nod
{
    int l,r, ans;
}node[N];
int a[N],f[N],d[N];
int n,m,t;
void getdata()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        char str[5];
        scanf("%d%d%s",&node[i].l,&node[i].r,str);
        if(str[0]=='o') node[i].ans=1;
        else node[i].ans=0;
        a[++t]=node[i].l-1;
        a[++t]=node[i].r;
    }
    sort(a+1,a+t+1);
    n=unique(a+1,a+t+1)-a-1;
}
int get(int x)
{
    if(x==f[x])
        return x;
    return f[x]=get(f[x]);
}
int main()
{
    getdata();
    for(int i=1;i<=2*n;i++)
        f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x= lower_bound(a+1,a+n+1,node[i].l-1) -a;
        int y=lower_bound(a+1,a+n+1,node[i].r) -a;
        int x_odd=x,x_even=x+n;
        int y_odd=y,y_even=y+n;
        if(node[i].ans==0)
        {
            if(get(x_odd)==get(y_even))
            {
               cout<<i-1<<endl;
                return 0;
            }
            f[get(x_odd)]=get(y_odd);
            f[get(x_even)]=get(y_even);
        }else
        {
            if(get(x_odd)==get(y_odd))
            {
                cout<<i-1<<endl;
                return 0;
            }
            f[get(x_odd)]=get(y_even);
            f[get(x_even)]=get(y_odd);
        }
    }
    cout<<m<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值