并查集 扩展域记录相关信息

标题 : 并查集 扩展域记录相关信息

小 A 和小 B 在玩一个游戏。

首先,小 A 写了一个由 0 和 1 组成的序列 S,长度为 N。

然后,小 B 向小 A 提出了 M 个问题。

在每个问题中,小 B 指定两个数 l 和 r,小 A 回答 S[l∼r] 中有奇数个 1 还是偶数个 1。

机智的小 B 发现小 A 有可能在撒谎。

例如,小 A 曾经回答过 S[1∼3] 中有奇数个 1,S[4∼6] 中有偶数个 1,现在又回答 S[1∼6] 中有偶数个 1,显然这是自相矛盾的。

请你帮助小 B 检查这 M 个答案,并指出在至少多少个回答之后可以确定小 A 一定在撒谎。

即求出一个最小的 k,使得 01 序列 S 满足第 1∼k 个回答,但不满足第 1∼k+1 个回答。

输入格式
第一行包含一个整数 N,表示 01 序列长度。

第二行包含一个整数 M,表示问题数量。

接下来 M 行,每行包含一组问答:两个整数 l 和 r,以及回答 even 或 odd,用以描述 S[l∼r] 中有偶数个 1 还是奇数个 1。

输出格式
输出一个整数 k,表示 01 序列满足第 1∼k 个回答,但不满足第 1∼k+1 个回答,如果 01 序列满足所有回答,则输出问题总数量。

数据范围
N≤109,M≤10000
输入样例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd
输出样例:
3

首先本题需要分析序列特性,我们发现序列是由零一组成得序列,所以l~r之间1的个数可以转化成前缀和s[r]-s[l-1],因为01序列中1的个数即使前缀和的值;
然后定义扩展域的含义:我们将X定义为s[x]中1的个数为偶数个,X+n定义为s[x]中1的个数为奇数个;若一个集合中一个事件成立,则其余事件必然成立;将问题分解为s[r]%2是否等于s[l-1]%2两个类别;
然后就可以开始做并查集了,(对了,本题还需要做离散化,可以用map或者哈希或者去重二分做离散化)当s[l-1]~s[r]中1的个数为偶数个时,显然s[l-1]中1的个数模2等于s[r]中1的个数模2;如果我们可以发现X与Y+N处于一个集合中时,那么就与题意产生矛盾;否则我们可以将离散话之后的L-1和R并入到一个集合中,L-1+N与R+N并入到一个集合;
情况二与情况一同理
添加链接描述

#include"iostream"
#include"cstdio"
#include"cstring"
#include"algorithm"
#include"unordered_map"
#include"string"
using namespace std;
const int N=4e4+10,M=N/2;
int f[N],n,m;
unordered_map<int,int>p;
int get(int x)
{
    if(!p.count(x))p[x]=++n;
    return p[x];
}
int find(int x)
{
    return f[x]==x?f[x]:f[x]=find(f[x]);
}
int main()
{
    cin>>n>>m;
    n=0;
    int res=m;
    for(int i=0;i<N;i++)f[i]=i;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        string op;
        cin>>a>>b>>op;
        a=get(a-1),b=get(b);
      if(op=="even")
       {
            if (find(a + M) == find(b))
            {
                res = i - 1;
                break;
            }
            f[find(a)] = find(b);
            f[find(a + M)] = find(b + M);
       }
          else
       {
           if (find(a) == find(b))
            {
                res = i - 1;
                break;
            }

            f[find(a + M)] = find(b);
            f[find(a)] = find(b + M);
       }
    }
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值