AcWing 239.奇偶游戏 (带权并查集/种类并查集)
-
题意:你和朋友玩游戏,有个一\(01\)序列,你每次给出一个区间,朋友会回答这个区间中的\(1\)的个数是奇数还是偶数,但是你亲爱的朋友可能在撒谎,问在哪个询问你能确定你的朋友在撒谎,输出回合数.
-
题解:假如区间\([l,r]\)所含的奇数个数为偶数的话,那么其前缀和\(s_{l-1}\)和\(s_r\)所含的\(1\)的个数一定同奇同偶,如果\([l,r]\)所含奇数个数为奇数,\(s_{l-1}\)和\(s_r\)奇偶性一定不同.
所以我们对前缀和\(s\)进行维护,如果\([l,r]\)为偶数,那么我们可以将区间\(s_{l-1}\)和\(s_r\)合并,并且它们之间的权值应该为\(0\),若为奇数则权值为\(1\),传递关系可以用异或来操作,核心思想依然是带权并查集,但是这题还需要离散化.
此处顺便附上种类并查集的做法
-
代码:
#include <iostream> #include <iomanip> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <vector> #include <map> #include <set> #include <unordered_set> #include <unordered_map> #define ll long long #define db double #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; int gcd(int a,int b){return b?gcd(b,a%b):a;} int lcm(int a,int b){return a/gcd(a,b)*b;} inline int read() { int X=0; bool flag=1; char ch=getchar(); while(ch<'0'|ch>'9') {if(ch=='-') flag=0; ch=getchar();} while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();} if(flag) return X; return ~(X-1); } int n; int m; int a,b; string op; int p[N]; int d[N]; unordered_map<int,int> S; int get(int x){ if(S.count(x)==0) S[x]=++n; return S[x]; } int find(int x){ if(p[x]!=x){ int root=find(p[x]); d[x]^=d[p[x]]; p[x]=root; } return p[x]; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); rep(i,1,10010) p[i]=i; cin>>n>>m; n=0; int ans=m; rep(i,1,m){ cin>>a>>b>>op; a=get(a-1),b=get(b); int fa=find(a); int fb=find(b); int t=0; if(op=="odd") t=1; if(fa==fb){ if((d[a]^d[b])!=t){ ans=i-1; break; } } else{ p[fa]=fb; d[fa]=d[a]^d[b]^t; } } cout<<ans<<'\n'; return 0; } ************************************************************** #include <iostream> #include <iomanip> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <vector> #include <map> #include <set> #include <unordered_set> #include <unordered_map> #define ll long long #define db double #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; int gcd(int a,int b){return b?gcd(b,a%b):a;} int lcm(int a,int b){return a/gcd(a,b)*b;} inline int read() { int X=0; bool flag=1; char ch=getchar(); while(ch<'0'|ch>'9') {if(ch=='-') flag=0; ch=getchar();} while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();} if(flag) return X; return ~(X-1); } int n,m; int p[N]; int a,b; string op; unordered_map<int,int> S; int get(int x){ if(S.count(x)==0) S[x]=++n; return S[x]; } int find(int x){ if(p[x]!=x) p[x]=find(p[x]); return p[x]; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>m; int cnt=10010/2; n=0; rep(i,1,10010) p[i]=i; int ans=m; rep(i,1,m){ cin>>a>>b>>op; //p[x]存偶数,p[x+n]存奇数 a=get(a-1),b=get(b); if(op=="even"){ if(find(a+cnt)==find(b)){ ans=i-1; break; } p[find(a)]=find(b); p[find(a+cnt)]=find(b+cnt); } else{ if(find(a)==find(b)){ ans=i-1; break; } p[find(a)]=find(b+cnt); p[find(a+cnt)]=find(b); } } cout<<ans<<'\n'; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮