【POJ1733】Parity game——楊子曰題目

【POJ1733】Parity game——楊子曰題目

Now and then you play the following game with your friend. Your friend writes down a sequence consisting of zeroes and ones. You choose a continuous subsequence (for example the subsequence from the third to the fifth digit inclusively) and ask him, whether this subsequence contains even or odd number of ones. Your friend answers your question and you can ask him about another subsequence and so on. Your task is to guess the entire sequence of numbers.

You suspect some of your friend’s answers may not be correct and you want to convict him of falsehood. Thus you have decided to write a program to help you in this matter. The program will receive a series of your questions together with the answers you have received from your friend. The aim of this program is to find the first answer which is provably wrong, i.e. that there exists a sequence satisfying answers to all the previous questions, but no such sequence satisfies this answer.
Input
The first line of input contains one number, which is the length of the sequence of zeroes and ones. This length is less or equal to 1000000000. In the second line, there is one positive integer which is the number of questions asked and answers to them. The number of questions and answers is less or equal to 5000. The remaining lines specify questions and answers. Each line contains one question and the answer to this question: two integers (the position of the first and last digit in the chosen subsequence) and one word which is either even' orodd’ (the answer, i.e. the parity of the number of ones in the chosen subsequence, where even' means an even number of ones andodd’ means an odd number).
Output
There is only one line in output containing one integer X. Number X says that there exists a sequence of zeroes and ones satisfying first X parity conditions, but there exists none satisfying X+1 conditions. If there exists a sequence of zeroes and ones satisfying all the given conditions, then number X should be the number of all the questions asked.
Sample Input

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

Sample Output

3

簡單描述一下題意:就是你和你的朋友在玩遊戲,他想了一個01序列,然後你按順序給他一些詢問對於每個詢問x,y,它會回答你這個01序列的區間[x,y]裏有奇數個1還是偶數個1,但是機智的你發現他在撒謊,讓你輸出他在回答第幾個問題時撒了謊 -1(別問我爲什麼要-1)


這是一道有趣的並查集題,我們解決這道題的數據結構叫做——帶權並查集(想要了解他它還有一道好題:[NOI2002]銀河英雄傳說

首先,我們能不能轉換一下回答的意思,如果我們記錄這個01串的前綴和是sum[i]

那麼回答區間[l,r]是偶數,也就是sum[y]-sum[x-1]是偶數,是不是就是告訴你:sum[x-1]和sum[y]的奇偶性相同
同樣的,如果回答區間[x,y]是奇數,就相當於告訴你:sum[x-1]和sum[y]的奇偶性不同

然後我們要看的就是這一些回答中有沒有矛盾有沒有發現就是並查集可以乾的事情,美滋滋!
我們把奇偶性相同的放在一個並查集裏,然後遇到奇偶性不同的情況判斷一下是不是不在同一個並查集裏就好了!完美!

BUT,有一個嚴重的事情:就是這道題有一堆噁心的傳遞性質:

  1. x1x_1x2x_2奇偶性相同,x2x_2x3x_3奇偶性也相同,那麼x1x_1x3x_3奇偶性相同。(不過這種情況用上面的方式是可以維護的)
  2. x1x_1x2x_2奇偶性相同,x2x_2x3x_3奇偶性不同,那麼x1x_1x3x_3奇偶性不同。(這種情況用上面的方式就不行了吧!!( •̀ ω •́ )✧)
  3. x1x_1x2x_2奇偶性不同,x2x_2x3x_3奇偶性也不不同,那麼x1x_1x3x_3奇偶性相同。(這種情況也不行吧!)

So,我們就要來用一個nb的數據結構來維護它們——帶權並查集

我們不管給的兩個數奇偶性是否相同,都要把並查集合並:

我們在並查集的節點i上記錄一個d[i],如果d[i]是1說明它和fa[i]的奇偶性相同,如果d[i]是0,則說明它和fa[i]奇偶性相同

第一種情況:如果x和y在同一個集合內,我們就可以通過d[x] xor d[y]可以確定x和y的奇偶性是否相同(如果是1則說明不同,如果是0則說明相同)

爲什麼捏,假設現在這個並查集長這樣:
在這裏插入圖片描述
(我們把d[i]記在i上面那條邊上,更加形象一點)

現在這張圖還是沒有經行路徑壓縮的,我們要先對x和y這兩條路徑壓縮一下,那麼壓縮以後這個d[i]怎麼計算捏?

先把結論告訴大家:
在這裏插入圖片描述
相信機智的你也一定發現了,壓縮後的d[i],就是一路壓縮上去的d[i]的異或和

原因也非常簡單:我們考慮某兩個點的奇偶性是否相同,比如a和b,它們都與c相連,那麼a,b,c之間的關係也就滿足裏上面寫的傳遞性質,有沒有發現剛好是d[a],d[b]異或的關係,那麼任意兩個點的關係就可把它們路徑上的點兩兩算出(有點類似數學歸納法),就是這條路徑上的異或和

int gf(int x){
    if (x==fa[x]) return x;
    int tmp=gf(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=tmp;
}

因爲x和y在同一集合,在路徑壓縮後fa[x]就等於fa[y]。x,y的奇偶性就可以通過上面的結論得到

得到x和y的奇偶性是否相同後和給出的答案比較一下,看看有沒有矛盾就行了

int check(int x,int y,int k){
    int fx=gf(x),fy=gf(y);
    if (fx==fy){
        if ((d[x]^d[y])!=k) return 0;
    }
    else merge(x,y,k);//這是我們在下面要討論的第二中情況
    return 1;
}

第二種情況:x,y如果不在同一集合怎麼辦

如果x和y不在同一集合,說明在此之前,x和y一點關係也沒有,這時是不可能出現矛盾的情況的,那我們就要考慮一下合併怎麼辦了

比方說我們已經路徑壓縮好了:
在這裏插入圖片描述

然後我們開始合併,我們不妨把fx的爸爸變成fy:
在這裏插入圖片描述
那我們現在就需要考慮一下d[fx]也就是這條紅色邊的d值怎麼算

首先我們已經知道x,y的奇偶性關係,我們還知道它們的奇偶性關係是可以通過路徑上的異或和算出的,於是我們就可以列出一個方程了(ans是x和y的奇偶性關係):
ans=d[x] xor d[y] xor d[fx]ans=d[x]\ xor\ d[y]\ xor\ d[fx]

我們就美滋滋地得到了:d[fx]=d[x] xor d[y] xor ansd[fx]=d[x]\ xor\ d[y]\ xor\ ans

然後我們就可以合併了

void merge(int x,int y,int k){
    int fx=gf(x),fy=gf(y);
    fa[fx]=fy;
    d[fx]=d[x]^d[y]^k;
}

OK,完事


c++代碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn=10005;

map<int,int> mp;

int fa[maxn*2],d[maxn*2],x[maxn],y[maxn];

char ch[maxn][10];

int gf(int x){
    if (x==fa[x]) return x;
    int tmp=gf(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=tmp;
}

void merge(int x,int y,int k){
    int fx=gf(x),fy=gf(y);
    fa[fx]=fy;
    d[fx]=d[x]^d[y]^k;
}

int check(int x,int y,int k){
    int fx=gf(x),fy=gf(y);
    if (fx==fy){
        if ((d[x]^d[y])!=k) return 0;
    }
    else merge(x,y,k);
    return 1;
}

int main(){
    int n,m,cnt=0;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=2*m;i++){
        fa[i]=i;
    }
    for (int i=1;i<=m;i++){
        scanf("%d%d%s",&x[i],&y[i],ch[i]);
    }
    for (int i=1;i<=m;i++){
        if (!mp[x[i]-1]) mp[x[i]-1]=++cnt;
        if (!mp[y[i]]) mp[y[i]]=++cnt;
        if (ch[i][0]=='e'){
            if(!check(mp[x[i]-1],mp[y[i]],0)){
                printf("%d",i-1);
                return 0;
            } 
        } 
        else{
            if(!check(mp[x[i]-1],mp[y[i]],1)){
                printf("%d",i-1);
                return 0;
            } 
        } 
    }
    printf("%d",m);
    return 0;
}

參考:

《算法競賽進階指南》 李煜東 著

於HG機房&TJQ高層小區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章