【奇偶传递关系 边带权】 奇偶游戏
传送门
题意
给定一个\(N\)表示一个\(01\)序列\(S\)的长度,给定\(M\)个指示,每个指示一个\(l、r\),以及说明关系的字符串
- \({l,r,odd}\),表示\((l,r)\)有偶数个\(1\)
- \({l,r,even}\),表示\((l,r)\)有奇数个\(1\)
求出在多少个指示后出现矛盾
数据范围
\(N \leq 10^{9}, M \leq 10000\)
题解
用\((l,r)\)的前缀和来表示其中\(1\)个数的奇偶性
知道了\(l,r\)的奇偶数,就知道了\(sum[l]\)和\(sum[r]\)之间的奇偶关系
- 当\((l,r)\)有奇数个\(1\)的时候,\(sum[l-1]\)和\(sum[r]\)的奇偶性不同
- 当\((l,r)\)有偶数个\(1\)的时候,\(sum[l-1]\)和\(sum[r]\)的奇偶性相同
奇偶性质的传递一共有三种
- 当\(x\)和\(y\)相同,\(y\)和\(z\)相同,\(x\)和\(z\)相同
- 当\(x\)和\(y\)相同,\(y\)和\(z\)不同,\(x\)和\(z\)不同
- 当\(x\)和\(y\)不同,\(y\)和\(z\)不同,\(x\)和\(z\)不同
序列的长度远大于需要给定关系中设计的区间大小,将其离散化为一个较小的区间进行维护
用一个数组记录当前区间首尾和根的关系,对于每一个指令
- 当前两个区间是否在同一个集合中,在同一个集合中前面就给出过定义,如果奇偶性不同,就找到了矛盾点
- 不在的话就合并,并按照给定的奇偶性定义二者的关系
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>a;i--)
const int N=1e5+10;
unordered_map<int,int>rec;
int n,m,l,r;
int cnt;
int fa[N],d[N];
int diff;
int find(int x)
{
if(fa[x]==x) return fa[x];
int root=find(fa[x]);
d[x] ^= d[fa[x]];// 还没有合并到根时需要和现在可能已经更新过的祖宗节点求传递关系
return fa[x]=root;
}
void merge(int a,int b,int diff)
{
int pa=find(a),pb=find(b);
if(pa!=pb)
{
fa[pa]=pb;
d[pa] = d[a]^d[b]^diff;
}
}
int disperse(int x)
{
if(!rec[x]) rec[x]=++cnt;
return rec[x];
}
int main()
{
cin>>n>>m;
rep(i,0,N) fa[i]=i;
char op[5];
int ans=m;
rep(i,1,m+1)
{
cin>>l>>r;
cin>>op;
if(op[0]=='o') diff=1;
else diff=0;
int ll=disperse(l-1),lr=disperse(r);
int pl=find(ll),pr=find(lr);
if(pl==pr)
{
if(d[ll]^d[lr] != diff)
{
ans=i-1;
break;
}
}
else merge(ll,lr,diff);
}
cout<<ans<<endl;
}