POJ1733 Parity game

POJ1733 Parity game

博客图片

题目链接

POJ1733 Parity game

题目概述

给出一段长度为 n n n的由 0 0 0 1 1 1组成的序列串,然后给出 m m m次条件,每个条件的形式是i j even(odd)表示区间 [ i , j ] [i,j] [i,j] 1 1 1的个数是偶数个还是奇数个,如果第 k + 1 k+1 k+1个条件在前面的 k k k个条件都满足的条件下无法成立,那么输出 k k k,如果所有的条件都能满足,那么输出 m m m,其中:
1 ≤ N ≤ 1 0 9 , 1 ≤ M ≤ 5 ⋅ 1 0 5 1\leq N \leq 10^9, 1\leq M \leq 5\cdot10^5 1N109,1M5105

思路分析

输入的格式隐含着 i ≤ j i \leq j ij,因为原始的输入区间是闭区间 [ i , j ] [i,j] [i,j],这里可以更改为 ( i − 1 , j ] (i-1,j] (i1,j]的形式,一方面便于计算从某个结点开始已知连续已知的区间 1 1 1的个数的奇偶性;另外一方面便于区间的合并,比如样例中的 ( 0 , 2 ] , ( 2 , 4 ] (0,2],(2,4] (0,2],(2,4]后面一个可以较为容易的与前面一个合并.合并连续区间根据的事实是:两个奇数相加是偶数,两个偶数相加认识偶数,一个奇数一个偶数相加是奇数.

最重要的一点是,题目中的输入的数据是可以依次连接拼成一个连续区间的,假设当前拼接后的连续的区间的最右面结点是 r o o t root root,对于此前任何一个区间片段的端点 x x x是可以计算出 [ x , r o o t ] [x,root] [x,root]这段区间内 1 1 1的个数的奇偶性的,这个区间任意另个合法的区间片段 [ i , j ] [i,j] [i,j]( i , j i,j i,j必须是此前出现过的区间片段的端点)都是可以计算出来的.

利用带权并查集可以计算出这样的某个端点 x x x到末端点(根节点) r o o t root root中间 1 1 1的数量的奇偶性,权重是结点 x x x到根节点 r o o t root root之间已知的 1 1 1是奇数还是偶数,每次find_set查询更新结点 x x x r o o t root root之间的 1 1 1的个数的奇偶性,因为计算 1 1 1的个数实际是结点 x x x r o o t root root路径的长度再加上 1 1 1(包括结点 x x x),所以为了可以以 d [ x − 1 ] d[x-1] d[x1]表示从结点 x x x到根节点 r o o t root root之间的 1 1 1的数量的奇偶性.读取每个约束条件,计算区间 [ i , j ] [i,j] [i,j](实际存储的是 ( i − 1 , j ] (i-1,j] (i1,j] 1 1 1出现次数的奇偶性.

代码

/*
 * @Author: Shuo Yang
 * @Date: 2020-08-02 15:22:26
 * @LastEditors: Shuo Yang
 * @LastEditTime: 2020-08-03 15:59:23
 * @FilePath: /Code/POJ/1733.cpp
 */
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int buf[N];
int s[N];
int d[N];
// int cnt[N];
bool flag = true;
int ans = 0;
int cnt = 0;

int find_set(int x){
    if(s[x] == x)
        return x;
    int root = find_set(s[x]);
    d[x] ^= d[s[x]];
    s[x] = root;
    return s[x];
}

void union_set(int x, int y, int value){
    int a = find_set(x);
    int b = find_set(y);
    if(a == b){
        if((d[x] ^ d[y]) != value){
            flag = false;
        }
        return;
    }
    s[a] = b;
    d[a] = d[x]^d[y]^value;
}

struct node{
    int left, right,nums;
};

node nodes[N];

int main(int argc, char** args){
    for(int i = 0; i < N; ++i)
        s[i] = i;
    ios::sync_with_stdio(false);
    cout.tie(0);
    cin.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i = 0; i < m; ++i){
        int a,b,temp;
        string str;
        cin>>a>>b>>str;
        if( str == "even" ){
            temp = 0;
        }else{
            temp = 1;
        }
        nodes[i]={a-1,b,temp};
        buf[cnt++] = a-1;
        buf[cnt++] = b;
    }
    sort(buf, buf+cnt);
    cnt = unique(buf, buf+cnt)-buf;
    for(int i = 0; i < m; ++i){
        int x =  nodes[i].left;
        int y = nodes[i].right;
        int value = nodes[i].nums;
        x = lower_bound(buf, buf+cnt, x)-buf;
        y = lower_bound(buf, buf+cnt, y)-buf;
        union_set(x, y, value);
        if( !flag ){
            cout<<i<<endl;
            break;
        }
    }
    if( flag )
        cout<<m<<endl;
    return 0;
}

两个地方需要注意,一是 N N N的规模比较大,可以先进行离散化处理,而是要在读入数据时进行一个简单的预处理,把原来 [ l e f t , r i g h t ] [left,right] [left,right]形式的输入改为 ( l e f t − 1 , r i g h t ] (left-1,right] (left1,right]这样的格式存储两个区间端点,然后利用异或运算简单计算区间的内部 1 1 1出现次数的奇偶性.(PS:事实上在后面检验约束项时候,我把int x = nodes[i].left;写成了int x = nodes[i].left-1;尽然也莫名其妙的过了,抑郁,而且是刚刚写这个时候才发现的┭┮﹏┭┮)

其它

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页