AcWing239.奇偶游戏(离散化 + 边带权/扩展域并查集)

题目:https://www.acwing.com/problem/content/description/241/

小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≤1e9,M≤10000

输入样例:

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd

输出样例:

3

解法一:边带权做法:首先考虑数据范围,所以要离散化,通过每个问答我们可以得到这样的信息:如果[l,r]是奇数,sum[r]与sum[l-1]的奇偶性不同,如果为偶数,奇偶性相同,(sum为前缀和数组),我们考虑维护一个数组d[i]表示 i 与 f[i] 的关系是否为同类,0代表同类,1代表不同类,当l和r在一个集合时,因为有同一个祖宗节点,所以它们这时的类别已经定型,如果与回答存在矛盾,可以输出答案,反之,如果不在一个集合,则进行合并,并且确定被合并的祖宗节点的类别,这里可以用异或位运算,我们可以省下许多判断,比如如果存在两个不在同一集合的点x,y,x的祖宗节点a的d[a] = d[x] ^ d[y] ^ query[i].op;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e4+5;
struct node {
    int x,y,op;
}query[N];
int d[N<<1],f[N<<1];
vector<int> q;
int find(int x) {
    if(x != f[x]) {
        int root = find(f[x]);
        d[x] ^= d[f[x]];
        f[x] = root;
    }
    return f[x];
}
int main() {
    int n,m,a,b;
    string op;
    cin >> n >> m;
    for(int i=0; i< N<<1; i++) f[i] = i;
    for(int i=0;i<m;i++) {
        int t = 0;
        cin >> a >> b >> op;
        if(op == "odd") t = 1;
        query[i] = {a,b,t};
        q.push_back(a-1);
        q.push_back(b);
    }
    sort(q.begin(),q.end());
    q.erase(unique(q.begin(),q.end()),q.end());
    for(int i=0;i<m;i++) {
        auto it = find(q.begin(),q.end(),query[i].x-1);//vector不能用lower_bound
        int x = distance(q.begin(),it);
        it = find(q.begin(),q.end(),query[i].y);
        int y = distance(q.begin(),it);
        a = find(x),b = find(y);
        if(a == b) {
            if((d[x]^d[y]) != query[i].op) {
                cout << i;
                return 0;
            }    
        }else {
            d[a] = query[i].op ^ d[x] ^ d[y];
            f[a] = b;
        }
    }
    cout << m;
    return 0;
}

 

解法二:扩展域并查集:我们依然先离散化,将一个点x变成两个点x_even,x_odd表示它们的偶数域和奇数域,其中x_even表示sum[x]为偶数,x_odd表示sum[x]为奇数,当query[i].op为奇数时,若出现x的偶数域和y的偶数域在同一集合,则存在矛盾,若不存在矛盾,则将x_odd的集合与y_even合并,x_even与y_odd合并,若query[i].op为偶数时,若出现x的奇数域和y的偶数域在同一集合,则存在矛盾,若不存在矛盾,则将x_odd的集合与y_odd合并,x_even与y_even合并。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e4+5;
struct node {
    int x,y,op;
}query[N];
int f[N<<1];
vector<int> q;
int find(int x) {
   if(x == f[x]) return x;
   return f[x] = find(f[x]);
}
int main() {
    int n,m,a,b;
    string op;
    cin >> n >> m;
    for(int i=0; i< N<<1; i++) f[i] = i;
    for(int i=0;i<m;i++) {
        int t = 0;
        cin >> a >> b >> op;
        if(op == "odd") t = 1;
        query[i] = {a,b,t};
        q.push_back(a-1);
        q.push_back(b);
    }
    sort(q.begin(),q.end());
    q.erase(unique(q.begin(),q.end()),q.end());
    for(int i=0;i<m;i++) {
        auto it = find(q.begin(),q.end(),query[i].x-1);
        int x = distance(q.begin(),it);
        it = find(q.begin(),q.end(),query[i].y);
        int y = distance(q.begin(),it);
        if(query[i].op) {
            if(find(x) == find(y)) {
                cout << i;
                return 0;
            }
            f[find(x)] = f[find(y + N)];
            f[find(x + N)] = f[find(y)];
        }else {
            if(find(x + N) == find(y)) {
                cout << i;
                return 0;
            }
            f[find(x)] = f[find(y)];
            f[find(x + N)] = f[find(y + N)];
        }
    }
    cout << m;
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值