poj1733 Parity game(离散化+并查集)

题目链接:http://poj.org/problem?id=1733
用sum数组表示序列的前缀和
1. s[l~r]有偶数个1 -> sum[l-r]和sum[r]奇偶性相同
2. s[l~r]有奇数个1 -> sum[l-r]和sum[r]奇偶性不同

有3种传递关系
1. x1与x2奇偶性相同,x2与x3奇偶性相同,则x1与x3奇偶性相同
2. x1与x2奇偶性相同,x2与x3奇偶性不同,则x1与x3奇偶性不同
3. x1与x2奇偶性不同,x2与x3奇偶性不同,则x1与x3奇偶性相同

由于序列长度较大,需要用离散化

struct
{
    int l, r, ans;
} query[10010];
int a[maxn],sett[maxn],val[maxn],n,m,t;

void read_discrete()   // 读入、离散化
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        char str[5];
        scanf("%d%d%s", &query[i].l, &query[i].r, str);
        if(str[0] == 'o')
        query[i].ans = 1;
        else
            query[i].ans = 0;
        a[++t] = query[i].l - 1;
        a[++t] = query[i].r;
    }
    sort(a + 1, a + t + 1);
    n = unique(a + 1, a + t + 1) - a - 1;
}

然后“边带权”和“扩展域”两种并查集都可以写

边带权
路径压缩:对x到树根路径上的所有边权做异或操作,即可得到x与树根的奇偶性,边权为0,代表相同,为1,不同

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 20010;

struct
{
    int l, r, ans;
} query[10010];
int a[maxn],sett[maxn],val[maxn],n,m,t;

void read_discrete()   // 读入、离散化
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        char str[5];
        scanf("%d%d%s", &query[i].l, &query[i].r, str);
        if(str[0] == 'o')
        query[i].ans = 1;
        else
            query[i].ans = 0;
        a[++t] = query[i].l - 1;
        a[++t] = query[i].r;
    }
    sort(a + 1, a + t + 1);
    n = unique(a + 1, a + t + 1) - a - 1;
}

int findSet(int x)
{
     if(x==sett[x])
        return x;
     int y = findSet(sett[x]);
     val[x] ^= val[sett[x]];
     return sett[x] = y;
}

int main()
{
    read_discrete();
    int flag = 0;
    for (int i = 1; i <= n; i++)
        sett[i] = i;
    for(int i = 1;i<=m;i++)
    {
        if(flag)
            continue;
         // 求出l-1和r离散化之后的值
        int x = lower_bound(a + 1, a + n + 1, query[i].l - 1) - a;
        int y = lower_bound(a + 1, a + n + 1, query[i].r) - a;
        int x1 = findSet(x);
        int y1 = findSet(y);
        if(x1==y1)
        {
            if((val[x]^val[y]) != query[i].ans)
                {
                    printf("%d\n",i-1);
                    flag = 1;
                }
        }
        else// 不在同一集合,合并
        {
            sett[x1] =y1;
            val[x1]= val[x] ^ val[y] ^ query[i].ans;
        }
    }
    if(!flag)
        cout << m << endl;
    return 0;
}

扩展域

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct { int l, r, ans; } query[10010];
int a[20010], sett[40010], n, m, t;
void read_discrete() { // 读入、离散化
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        char str[5];
        scanf("%d%d%s", &query[i].l, &query[i].r, str);
        query[i].ans = (str[0] == 'o' ? 1 : 0);
        a[++t] = query[i].l - 1;
        a[++t] = query[i].r;
    }
    sort(a + 1, a + t + 1);
    n = unique(a + 1, a + t + 1) - a - 1;
}
int findSet(int x) {
    if (x == sett[x]) return x;
    return sett[x] = findSet(sett[x]);
}
int main() {
    read_discrete();
    for (int i = 1; i <= 2 * n; i++) sett[i] = i;
    for (int i = 1; i <= m; i++) {
        // 求出l-1和r离散化之后的值
        int x = lower_bound(a + 1, a + n + 1, query[i].l - 1) - a;
        int y = lower_bound(a + 1, a + n + 1, query[i].r) - a;
        int x_odd = x, x_even = x + n;
        int y_odd = y, y_even = y + n;
        if (query[i].ans == 0) { // 奇偶性相同
            if (findSet(x_odd) == findSet(y_even)) { // 矛盾
                cout << i - 1 << endl;
                return 0;
            }
            sett[findSet(x_odd)] = findSet(y_odd); // 合并
            sett[findSet(x_even)] = findSet(y_even);
        }
        else { // 奇偶性不同
            if (findSet(x_odd) == findSet(y_odd)) { // 矛盾
                cout << i - 1 << endl;
                return 0;
            }
            sett[findSet(x_odd)] = findSet(y_even); // 合并
            sett[findSet(x_even)] = findSet(y_odd);
        }
    }
    cout << m << endl; // 没有矛盾
}

猜你喜欢

转载自blog.csdn.net/qq_39239844/article/details/81050415
今日推荐