奇偶遊戲-並查集

小A和小B在玩一個遊戲。
首先,小A寫了一個由0和1組成的序列S,長度爲N。
然後,小B向小A提出了M個問題。
在每個問題中,小B指定兩個數 l 和 r,小A回答 S[l~r] 中有奇數個1還是偶數個1。
機智的小B發現小A有可能在撒謊。
例如,小A曾經回答過 S[1~3] 中有奇數個1, S[4~6] 中有偶數個1,現在又回答 S[1~6] 中有偶數個1,顯然這是自相矛盾的。
請你幫助小B檢查這M個答案,並指出在至少多少個回答之後可以確定小A一定在撒謊。
即求出一個最小的k,使得01序列S滿足第1k個回答,但不滿足第1k+1個回答。
輸入格式
第一行包含一個整數N,表示01序列長度。
第二行包含一個整數M,表示問題數量。
接下來M行,每行包含一組問答:兩個整數l和r,以及回答“even”或“odd”,用以描述S[l~r] 中有奇數個1還是偶數個1。
輸出格式
輸出一個整數k,表示01序列滿足第1k個回答,但不滿足第1k+1個回答,如果01序列滿足所有回答,則輸出問題總數量。
數據範圍
N≤109,M≤10000N≤109,M≤10000
輸入樣例:
10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd

輸出樣例:
3

第一種方法及思路:帶邊權寫法:首先我們觀察到數比較大,但是數的個數卻比較少,所以我們這裏可以使用離散化
,每次輸入一個區間的左右端點,和這個區間中1的個數是奇數還是偶數,如果這裏
類比前綴和的思想,如果s[n]表示前n個數中1的個數是奇數還是偶數,那麼區間n到m
的1的個數取決於s[m]與s[n-1]中1的個數,如果這倆個前綴和中1的個數都是奇數或者
都是偶數的話,那麼他們的差1的個數一定是偶數,否則,他們的差的個數就是奇數。
所以這裏如果區間[n,m]的差1的個數爲偶數的話,m和n-1這裏算爲同一種類型 ,如果他們的
區間內1的個數爲奇數的話,說明s[n-1]和s[m]的1的個數不同,這裏把他們算作不同的
類型,維護一個d數組,如果倆個數是一個類型,一個爲祖宗節點,另一個點到祖宗節點
的距離爲偶數,如果不是一個類型的話,也弄成一個集合,但是到祖宗節點的距離此時便
是一個奇數,每次集合中加入新的點時都得維護新的點到祖宗節點的距離,然後把所有輸入過的數都放到一個集合,如果後輸入的數與前面輸入的數出現
問題,則結束 。
ac代碼如下:

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<unordered_map>
using namespace std;
const int N=20010;
int n,m;
int p[N],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(){
 cin>>n>>m;
 n=0;
 for(int i=0;i<=N;i++)   p[i]=i;
 int res=m;
 for(int i=1;i<=m;i++){
  int a,b;
  string type;
  cin>>a>>b>>type;
  a=get(a-1),b=get(b);
  int t=0;
  if(type=="odd")   t=1;//如果類型相同,t=0,不同,t=1,這裏就要一直演算。 
  int pa=find(a),pb=find(b);
  if(pa==pb){
   if(((d[a]+d[b])%2+2)%2!=t){
    res=i-1;
    break;
   }
  }
  else{
   p[pa]=pb;
   d[pa]=d[a]^d[b]^t;
  }
 }
 cout<<res<<endl;
 return 0;
}

第二種方法及思路:擴展域寫法:這裏同樣一開始是離散化,然後依然拿m和n-1的關係找,這裏存儲的是一種條件,a代表a是偶數
a+BASE代表 a此時是一個奇數,同樣b也是這樣,如果他們屬於同一種類型或者不屬於 同
一類型,分別找出他們的關係,如果發生矛盾,停止循環。
ac代碼如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<unordered_map>
using namespace std;
const int N=40010,BASE=N/2;
int n,m;
int p[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(x!=p[x])   p[x]=find(p[x]);
 return p[x];
}
int main(){
 cin>>n>>m;
 n=0;
 for(int i=0;i<N;i++)   p[i]=i;
 int res=m;
 for(int i=1;i<=m;i++){
  int a,b;
  string type;
  cin>>a>>b>>type;
  a=get(a-1),b=get(b);
  if(type=="even"){
   if(find(a+BASE)==find(b)){
    res=i-1;
    break;
   }
   p[find(a)]=find(b);
   p[find(a+BASE)]=find(b+BASE);
  }
  else{
   if(find(a)==find(b)){
    res=i-1;
    break;
   }
   p[find(a+BASE)]=find(b);
   p[find(a)]=find(b+BASE);
  }
 }
 cout<<res<<endl;
 return  0;
} 
發佈了42 篇原創文章 · 獲贊 33 · 訪問量 3958
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章