Acm
6 Mar 2019

我曾以为我会并查集

POJ - 1733 Parity game

我也不知道我咕了多久,这个月发生了好多事情,首先是庄学姐甩锅,我们要自立更生了,然后最近蓝桥杯自闭,今天又气走了大奶牛。。。18级前途渺茫啊。。。

说一下这个题吧。。我曾经以为我会并查集。。但是我今天发现我错了。。。

这个题蓝书上有两种做法,代码基本看懂后照着打

sum[ x ] 表示 1 ~ x 中1的个数

那么,在 l ~ r 中如果1的个数为奇数,则说明 sum[ l - 1 ] , sum[ r ] 奇偶性不同,反之则说明相同。

于是用0表示两个点的奇偶性相同,1表示奇偶性不同

因为读入的 l , r 的范围为1e9,但是对问题产生影响的只有其中的1的个数的奇偶性,所以 l , r 之间的数字并没有用处,于是要先进行离散化

边带权

前面做了一道边带权的并查集,所以这个方法理解起来还算是比较容易

所谓边带权就是把一个并查集当作一颗树,然后每条边上都有一个权值,如果不用并查集的话,直接在树上搜索会用很长时间,所以并查集里的路径压缩是必要的

但是路径压缩后,原本的边就变成了一个直接指向根节点的边,所以新边的边权应该等于原先该点到根节点的路径上边权的和,用一个d数组记录到根节点的距离,边权级可以随着路径一起压缩

设一条边的边权为0代表两个点的奇偶性相同,为1代表奇偶性不同,则路径压缩时只要对边权进行异或运算,就可以将边权同时压缩

对两个点 x = l - 1 , y = r ,令 p = get (x) , q = get (y)ans 表示该问题的回答(由 0,1 来表示)

如果p=q,则两个点在同一个并查集中,如果连个点之间的奇偶性关系与ans不同,则小A在撒谎,否则就跳过

如果p!=q,则两个点不再同一个并查集中,所以需要把他们连起来,先使 ** fa[p]=q** ,对于边权来说,x与y之间的路径由 x ~ p , p ~ q , q ~ y 三部分组成,所以 ans=d[x]\^d[y]\^d[p]d[p]=d[x]\^d[y]\^ans

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define ll  long long
#define ull unsigned long long
using namespace std;
const int N=2e4+5,M=10007;
const ull base=13331;
const double Pi=acos(-1.0);
const ll C=299792458;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct node{
    int l,r,ans;
}query[N];
int a[N],fa[N],d[N];
void read_discrete(){
    cin>>n>>m;
    int t=0;
    for(int i=1;i<=m;i++){
        cin>>query[i].l>>query[i].r;
        string s;
        cin>>s;
        query[i].ans=((s[0]=='o')?1:0);
        a[++t]=query[i].l;
        a[++t]=query[i].r;
    }
    sort(a+1,a+t+1);
    n=unique(a+1,a+t+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()
{
    ios::sync_with_stdio(false);
    read_discrete();
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        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 p=get(x),q=get(y);
        if(p==q){
            if((d[x]^d[y])==query[i].ans)continue;
            cout<<i-1<<endl;
            return 0;
        }
        fa[p]=q;
        d[p]=d[x]^d[y]^query[i].ans;
    }
    cout<<m<<endl;
}

扩展域

其实这个题还是用扩展域比较好理解一些

对于两个点 x , y ,他们之间是无法同时表示奇偶性相同与奇偶性不同的,所以可以把一个点拆分成 x_oddx_even 两个点,那么两个点奇偶性相同就可以表示成 get(x_odd)==get(y_odd) , get(x_even)==get(y_even) ,奇偶性不同就是 get(x_odd)==get(y_even) , get(x_even)==get(y_odd)

所以查询一个询问,如果与之前的奇偶性关系矛盾,那么这个回答就是错误的

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define ll  long long
#define ull unsigned long long
using namespace std;
const int N=2e4+5,M=10007;
const ull base=13331;
const double Pi=acos(-1.0);
const ll C=299792458;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct node{
    int l,r,ans;
}query[N];
int fa[N*2];
int a[N];
void read_discrete(){
    cin>>n>>m;
    int t=0;
    for(int i=1;i<=m;i++){
        cin>>query[i].l>>query[i].r;
        string s;
        cin>>s;
        query[i].ans=((s[0]=='o')?1:0);
        a[++t]=query[i].l;
        a[++t]=query[i].r;
    }
    sort(a+1,a+t+1);
    n=unique(a+1,a+t+1)-a-1;
}
int get(int x){
    if(x==fa[x])return x;
    return fa[x]=get(fa[x]);
}
int main()
{
    ios::sync_with_stdio(false);
    read_discrete();
    for(int i=1;i<=2*n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        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(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);
        }
        else{
            if(get(x_even)==get(y_even)){
                cout<<i-1<<endl;
                return 0;
            }
            fa[get(x_odd)]=get(y_even);
            fa[get(x_even)]=get(y_odd);
        }
    }
    cout<<m<<endl;
}

Tags: