奇偶游戏(二分图染色法/带权并查集/扩展域并查集)
作者:互联网
题目链接:https://www.acwing.com/problem/content/241/
题目描述
简要题解
一.二分图染色法
首先,我们需要将题意所维护的信息转化一下,对于区间信息来说一定是不容易维护的,因此我们尝试转化为
端点维护,可以发现,若[l,r]中1的个数为偶数,则等同于[1,l-1]和[1,r]的1的个数同奇偶。因此我们有如
下转换:
- [l,r]中1的个数为偶数:[1,l-1],[1,r]同奇偶
- [l,r]中1的个数为奇数:[1,l-1],[1,r]不同奇偶
对于每个点,此时有奇和偶两种状态,此时的题相当于,需要将所有点分成两列,并且同一列内部是同奇偶性,不同列之间是不同奇偶。看什么时候会出现矛盾。因此我们可以用二分图判定来解决。因此思路为:
- 因为N为1e9,首先进行离散化
- 建无向图,若l-1和r同奇偶,设边权为0。若l-1和r不同奇偶,设边权为1。
- 二分跑logn次染色确定最早出现矛盾的地方。因为染色法不是通过给定顺序,而是通过边dfs来遍历的,即使设定上序号,也不好判断出第一个出现矛盾的序号。
题解代码
//二分图染色法
#include <bits/stdc++.h>
using namespace std;
const int N=20010;
vector<int>v;
int h[N],e[N],ne[N],idx,w[N];
int color[N],n;
int find(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
struct node
{
int l,r,x;
}sz[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
bool dfs(int u,int c)
{
color[u]=c;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!color[j])
{
if(w[i]==1)
{
if(!dfs(j,3-c))return false;
}
else if(!dfs(j,c))return false;
}
else
{
if(w[i]==1)
{
if(color[u]==color[j])return false;
}
else
{
if(color[u]!=color[j])return false;
}
}
}
return true;
}
bool check(int x)
{
memset(h,-1,sizeof h);
memset(color,0,sizeof color);
idx=0;
for(int i=1;i<=x;i++)
{
add(sz[i].l,sz[i].r,sz[i].x);
add(sz[i].r,sz[i].l,sz[i].x);
}
int flag=0;
for(int i=1;i<=v.size();i++)
{
if(!color[i])
{
if(!dfs(i,1))return false;
}
}
return true;
}
int main(void)
{
cin>>n;
cin>>n;
for(int i=1;i<=n;i++)
{
int a,b;
string str;
cin>>a>>b>>str;
v.push_back(a);
v.push_back(a-1);
v.push_back(b);
sz[i]={a,b,str[0]=='e'?0:1};
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
sz[i].l=find(sz[i].l-1);
sz[i].r=find(sz[i].r);
}
int l=0,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l<<endl;
system("pause");
return 0;
}
二.带权并查集
对于此题,我们也可以用带权并查集来解决,带权并查集的核心就是通过维护x,y到根的相对位置,来确定x,y之间的关系。 同样的,先将题目转化成维护l-1和r两个端点信息。顺序遍历所有的语句。此时有如下递推式:题解代码
//带权并查集
#include <bits/stdc++.h>
using namespace std;
const int N=20010;
vector<int>v;
int h[N],e[N],ne[N],idx,d[N];
int p[N*4],n;
int f(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
struct node
{
int l,r,x;
}sz[N];
int find(int a)
{
if(p[a]!=a)
{
int t=p[a];
p[a]=find(p[a]);
d[a]^=d[t];
}
return p[a];
}
int main(void)
{
cin>>n;
cin>>n;
for(int i=1;i<=4*n;i++)p[i]=i;
for(int i=1;i<=n;i++)
{
int a,b;
string str;
cin>>a>>b>>str;
v.push_back(a);
v.push_back(a-1);
v.push_back(b);
sz[i]={a,b,str[0]=='e'?0:1};
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
sz[i].l=f(sz[i].l-1);
sz[i].r=f(sz[i].r);
}
int flag=0;
for(int i=1;i<=n;i++)
{
int x=sz[i].l,y=sz[i].r,z=sz[i].x;
int pa=find(x),pb=find(y);
if(!z)
{
if(pa==pb)
{
if(d[x]^d[y])
{
cout<<i-1<<endl;
flag=1;
break;
}
}
else
{
d[pa]=d[x]^d[y];
p[pa]=pb;
}
}
else
{
if(pa==pb)
{
if(d[x]^d[y]==0)
{
cout<<i-1<<endl;
flag=1;
break;
}
}
else
{
d[pa]=d[x]^d[y]^1;
p[pa]=pb;
}
}
}
if(!flag)cout<<n<<endl;
system("pause");
return 0;
}
三.扩展域并查集
扩展域并查集解决此题相对于前两种更加好理解,扩展域并查集对于条件来划分集合,引申出x和x+n点,此处x点表示为x是奇数, x+n表示x为偶数,因此我们可以开始讨论:题解代码
//扩展域并查集
#include <bits/stdc++.h>
using namespace std;
const int N=20010;
vector<int>v;
int h[N],e[N],ne[N],idx,d[N];
int p[N*4],n;
int f(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
struct node
{
int l,r,x;
}sz[N];
int find(int a)
{
if(p[a]!=a)
{
int t=p[a];
p[a]=find(p[a]);
d[a]^=d[t];
}
return p[a];
}
int main(void)
{
cin>>n;
cin>>n;
for(int i=1;i<=4*n;i++)p[i]=i;
for(int i=1;i<=n;i++)
{
int a,b;
string str;
cin>>a>>b>>str;
v.push_back(a-1);
v.push_back(b);
sz[i]={a,b,str[0]=='e'?0:1};
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=1;i<=n;i++)
{
sz[i].l=f(sz[i].l-1);
sz[i].r=f(sz[i].r);
}
int flag=0;
for(int i=1;i<=n;i++)
{
int x=sz[i].l,y=sz[i].r,z=sz[i].x;
int pa1=find(x),pa2=find(x+n);
int pb1=find(y),pb2=find(y+n);
if(!z)
{
if(pa1==pb2)
{
cout<<i-1;
flag=1;
break;
}
p[pa1]=pb1;
p[pa2]=pb2;
}
else
{
if(pa1==pb1)
{
cout<<i-1;
flag=1;
break;
}
p[pa1]=pb2;
p[pa2]=pb1;
}
}
if(!flag)cout<<n<<endl;
system("pause");
return 0;
}
标签:奇偶,begin,return,sz,int,查集,带权,end,find 来源: https://blog.csdn.net/dylolorz/article/details/118462675