1. 程式人生 > >POJ 1733【奇偶遊戲】(邊帶權擴展域並查集)

POJ 1733【奇偶遊戲】(邊帶權擴展域並查集)

前綴 oid pre 合並 none 相同 ans 滿足 closed

我們用sum數組來表示序列S的前綴和,那麽在每次的回答中:

1 S[l~r]有偶數個1,等價於sum[l-1]與sum[r]的奇偶性相同。

2 S[l~r]有偶數個1,等價於sum[l-1]與sum[r]的奇偶性不同。

我們有如下傳遞關系:

1.若x1與x2奇偶性相同,x2與x3奇偶性也相同,那麽x1與x3的奇偶性也相同。

2. 若x1與x2奇偶性相同,x2與x3奇偶性不同,那麽x1與x3的奇偶性不同。

3.2. 若x1與x2奇偶性不同,x2與x3奇偶性不同,那麽x1與x3的奇偶性相同。

另外本體序列長度N很大,但M較小,可以先對數據離散化,

第一種 邊帶權並查集

上面的傳遞關系和異或操作很相似,我們考慮一下異或操作。

邊權d[x]為0,表示x與fa[x]的奇偶性相同,d[x]為1,表示不同,我們在路徑壓縮時,對x到樹根路徑上的所有邊權做異或運算,即可得到x與樹根的奇偶關系。

設l-1與r離散化後得到的值為x,y。

若x,y在一個集合,d[x]^d[y]既x和y的奇偶關系。若d[x]^d[y]!=ans,則在撒謊。

若x,y不在一個集合,則合並兩個集合,同時更新新邊的邊權d[p]=d[x]^d[y]^ans.

技術分享圖片
//邊帶權並查集
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int fa[N], a[N], tot,n,m,d[N];
struct node{ int l, r, ans; }b[N]; void read()//讀入 離散化 { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { char s[6]; scanf("%d%d%s", &b[i].l, &b[i].r, s); b[i].ans = (s[0] == o ? 1 : 0);//奇數個s[l-1]與s[r]奇偶性不相同,偶數個相同 a[++tot] = b[i].l - 1
; a[++tot] = b[i].r; } sort(a + 1, a + tot + 1); tot = unique(a + 1, a + tot + 1) - a - 1; } int get(int x) { if (x == fa[x]) return x; int root = get(fa[x]); d[x] ^= d[fa[x]]; return fa[x] = root; } int main() { read(); for (int i = 0; i <= tot; i++) fa[i] = i; int x, y; for (int i = 1; i <= m; i++) { //求出l-1和r離散化後的值 x = lower_bound(a + 1, a + tot + 1, b[i].l - 1) - a; y = lower_bound(a + 1, a + tot + 1, b[i].r) - a; //執行get函數,得到樹根,並進行路徑壓縮 int p = get(x), q = get(y); if (p == q)//已在同一集合 { if (d[x] ^ d[y] != b[i].ans)//不滿足 { cout << i - 1 << endl; return 0; } } else//不在同一集合,合並 { fa[p] = q; d[p] = d[x] ^ d[y] ^ b[i].ans; } } cout << m << endl; return 0; }
View Code

第二種 擴展域並查集

把每個點x拆成兩個節點x_odd,x_even,x_odd表示sum[x]為奇數,x_even表示偶數,這兩個節點分別是x的奇數域和偶數域

技術分享圖片
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int fa[2*N], a[N], tot,n,m;
struct node{
    int l, r, ans;
}b[N];
int get(int x)
{
    if (x == fa[x]) return x;
    return fa[x] = get(fa[x]);
}
void read()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        char s[6];
        scanf("%d%d%s", &b[i].l, &b[i].r, s);
        b[i].ans = (s[0] == o ? 1 : 0);
        a[++tot] = b[i].l - 1;
        a[++tot] = b[i].r;
    }
    sort(a + 1, a + tot + 1);
    tot = unique(a + 1, a + tot + 1) - a - 1;
}
int main()
{
    read();
    for (int i = 0; i <= 2*tot; i++)
        fa[i] = i;
    int x, y;
    for (int i = 1; i <= m; i++)
    {
        x = lower_bound(a + 1, a + tot + 1, b[i].l - 1) - a;
        y = lower_bound(a + 1, a + tot + 1, b[i].r) - a;
        int x_odd = x, x_even = x + tot;
        int y_odd = y, y_even = y + tot;
        if (b[i].ans)
        {
            if (get(x_odd)==get(y_odd))
            {
                cout << i - 1 << endl;
                return 0;
            }
            fa[get(x_odd)] = get(y_even);
            fa[get(x_even)] = get(y_odd);
        }
        else
        {
            if (get(x_odd) == get(y_even))
            {
                cout << i - 1 << endl;
                return 0;
            }
            fa[get(x_odd)] = get(y_odd);
            fa[get(x_even)] = get(y_even);
        }
    }
    cout << m << endl;
    return 0;
}
View Code

POJ 1733【奇偶遊戲】(邊帶權擴展域並查集)