[题解/模板]POJ_1733_Pairty game(带权并查集/扩展域

好题,题解来自书

1.设$sum$为前缀异或,$l~r$的奇偶性可以用$sum[r]^sum[l-1]$来表示,1为奇,0为偶

题目转化为给定一些$sum$之间的关系(形如sumi ^ sumj == 1/0),问什么时候出现矛盾

与程序自动分析一题有些相似,但是关系不是简单的相等或不等,而是异或

1.带权并查集(我也不知道怎么想到的

设$d[x]$为边权,0表示与父亲奇偶性相同,在路径压缩时维护,某点到根的边权即为某点到根路径上边权异或的值,合并$x,y$所在的两个集合时,设两个根为$xx,yy$,若让$xx$做$yy$的儿子,他们之间的边权需要由给出的$x,y$的关系决定,即$d[x]^d[xx]^d[y]==q[i].ans$,这样$d[xx]=d[x]^d[y]^q[i].ans$

//#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10009;
int n,m;
struct node{
    int l,r,f;
}q[maxn];
int hsh[maxn],cnt;
int d[maxn*2],fa[maxn*2];//d为边权0为与父亲奇偶性相同 
int find(int x){
    if(x==fa[x])return x;
    int rt=find(fa[x]);d[x]^=d[fa[x]];
    return fa[x]=rt;
}
int main(){
    scanf("%d%d",&n,&m);char op[10];
    for(int i=1;i<=2*m;i++)fa[i]=i;
    for(int i=1;i<=m;i++) {
        scanf("%d%d",&q[i].l,&q[i].r);
        scanf("%s",op);
        if(op[0]=='e')q[i].f=0;
        else q[i].f=1;
        hsh[++cnt]=q[i].l-1;//-1是因为前缀和 
        hsh[++cnt]=q[i].r;
    }
    sort(hsh+1,hsh+1+cnt);
    cnt=unique(hsh+1,hsh+1+cnt)-hsh-1;
    for(int i=1;i<=m;i++){
        int x=lower_bound(hsh+1,hsh+1+cnt,q[i].l-1)-hsh;
        int y=lower_bound(hsh+1,hsh+1+cnt,q[i].r)-hsh;
        int xx=find(x),yy=find(y);
        if(xx==yy){
            if((d[x]^d[y])!=q[i].f){
                printf("%d",i-1);return 0;
            }
        }
        else {
            fa[xx]=yy;d[xx]=d[x]^d[y]^q[i].f;
        }
    }
    printf("%d",m);
}

2.扩展域

每个点拆成两个点分别表示这个点是奇数和偶数/1和0,即奇数域和偶数域

根据奇偶性/异或的传递性,若$q[i].ans==1$,就把$x$和$y$的奇点和偶点互相相连,不然就把奇点和奇点,偶点和偶点相连,

代码已钴

原文地址:https://www.cnblogs.com/superminivan/p/11536739.html