acwing-239-奇偶遊戲(離散化+字首和+帶權並查集)+acwing164可達性統計(bitset使用+拓撲排序)
小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滿足第1~k個回答,但不滿足第1~k+1個回答。
解:
1,n的範圍在1e9,但是m的範圍在1e4,m次輸入每次最多兩個數,所以資料大概在2e4,需要離散化一下,將不同資料對映進去;
2,使用字首和 和 並查集處理問題
計算l和r範圍內1的數有奇數個還是偶數個,即為計算sum【r】-sum[l-1];,具體的題中不用進行計算...
並查集即為字首和中每個元素的奇偶性(奇數-奇數=偶數,偶數-偶數=偶數,奇偶性不同時,會出現差值為奇數的情況)
所有字首和元素會合併到一個祖先之中,d[x]維護x與根節點奇偶性是否相同
如果兩個元素已經合併在同一個祖先下,那麼就可以根據他們與祖先的奇偶性異同,得到他們的異同,判斷他們與輸入的異同是否一致 如果不一致就是發生矛盾,輸出答案...
%2!=&1
異或是相同為0,不同為1,奇數末尾&1=0,偶數末尾&1=1;
奇數%2=1,偶數%2=0...
程式碼:
#include<bits/stdc++.h> const int maxn=1e6+10; typedef long long ll; using namespace std; ll n; int m; int fa[maxn],d[maxn]; int cnt=0; unordered_map<int ,int> mp; int get(int x)//離散化 { if(mp.count(x)==0) mp[x]=++cnt;return mp[x]; } void init() { for(int i=1; i<=maxn-10; i++) fa[i]=i; } int find(int x) { if(x!=fa[x]) { int root=find(fa[x]); d[x]+=d[fa[x]]%2;//判斷奇偶 fa[x]=root; } return fa[x]; } int main() { cin>>n>>m; int ans=m; init(); for(int i=1; i<=m; i++) { int x,y; string s; cin>>x>>y>>s; x=get(x-1),y=get(y); int fx=find(x),fy=find(y); if(fx==fy) { if(s=="even") { //奇偶性相同 if((d[x]+d[y])%2!=0)//奇數,有矛盾 { ans=i-1; break; } } else if(s=="odd") {//奇偶性不同 if((d[x]+d[y])%2!=1)//偶數,有矛盾 { ans=i-1; break; } } } else {//合併 fa[fx]=fy;//0,1區分奇偶 int add=0; if(s=="odd") add=1; d[fx]=(d[x]+d[y]+add)%2; } } cout<<ans<<endl; system("pause"); return 0; }
acwing164可達性統計(bitset使用+拓撲排序)
題意:統計一個點能到多少個點,輸出
解:
首先用拓撲排序對點進行排序,再使用bitset進行位運算壓縮,求並集。
bitset的使用:
類似於bool,但是它的每個位置只佔1bit(特別小)
bitset的原理大概是將很多數壓成一個,從而節省空間和時間
一般來說bitset會讓你的演算法複雜度/32
定義bitset<maxn> bit;
bitset支援所有的位運算;
對於一個叫做bit的bitset:
bit.size() 返回大小(位數)
bit.count() 返回1的個數
bit.any() 返回是否有1
bit.none() 返回是否沒有1
bit.set() 全都變成1
bit.set(p) 將第p + 1位變成1(bitset是從第0位開始的!)
bit.set(p, x) 將第p + 1位變成x
bit.reset() 全都變成0
bit.reset(p) 將第p + 1位變成0
bit.flip() 全都取反
bit.flip(p) 將第p + 1位取反
bit.to_ulong() 返回它轉換為unsigned long的結果,如果超出範圍則報錯
bit.to_ullong() 返回它轉換為unsigned long long的結果,如果超出範圍則報錯
bit.to_string() 返回它轉換為string的結果
此題用到bit.count(),bit.reset(),當然此題的bit是一個數組;
程式碼:
#include<bits/stdc++.h> const int maxn=3e4+10; typedef long long ll; using namespace std; vector<int> G[maxn]; vector<int> Tup; bitset<maxn> F[maxn]; int in[maxn]; int n, m; void Tupo() { queue<int> que; for (int i = 1;i <= n;i++) { if (in[i] == 0) que.push(i); } while (!que.empty()) { int node = que.front(); que.pop(); Tup.push_back(node); for (int i = 0;i < G[node].size();i++) { int to = G[node][i]; if (--in[to] == 0) que.push(to); } } } void Solve() { for (int i = n-1;i >= 0;i--) { int x = Tup[i]; F[x].reset(); F[x][x] = 1; for (int k = 0;k < G[x].size();k++) { F[x] |= F[G[x][k]]; } } } int main() { scanf("%d%d", &n, &m); int u, v; for (int i = 1;i <= m;i++) { scanf("%d%d", &u, &v); G[u].push_back(v); in[v]++; } Tupo(); Solve(); for (int i = 1;i <= n;i++) printf("%d\n", (int)F[i].count()); return 0; }