【洛谷4443】[COCI2017-2018#3] Dojave(雜湊)
阿新 • • 發佈:2021-06-11
- 給定一個\(0\sim 2^n-1\)的排列,求有多少個區間,滿足在任意交換原序列中兩個不同的數之後這個區間的異或和可以為\(2^n-1\)。
- \(n\le20\)
不合法的判定條件
這種題目直接判合法顯然不太容易,因此我們考慮什麼樣的情況不合法。
注意到一個極其重要的性質就是這道題中的數包含了完整的值域。
因此,假設一個不合法區間原本的異或和為\(S\),那麼對於其中的任意一個數\(x\),都需要滿足\(S\oplus x\oplus(2^n-1)\)在這個區間中。
於是,我們認為異或和為\(S\oplus(2^n-1)\)的兩個數能夠配對。
顯然,如果區間長度為奇數肯定會有至少一個數無法達成配對。
如果區間長度模\(4\)餘\(2\),相當於有奇數個配對,每個配對異或和為\(S\oplus(2^n-1)\),它們的總異或和仍舊是\(S\oplus(2^n-1)\),顯然不等於\(S\),因此不存在。
於是區間長度必然是\(4\)的倍數,此時有偶數個配對,那麼它們的總異或\(S\)就等於\(0\),因此配對的條件就是異或和為\(2^n-1\),與具體是什麼區間根本沒有關係。
綜上,不合法的判定條件就是區間長度是\(4\)的倍數且區間中的數能夠實現配對。
雜湊
我們可以給能夠配對的兩個數隨機同一個權值,那麼區間中的數能夠實現配對可以認為就是要滿足區間異或和為\(0\)。
給雜湊陣列做個字首異或和,就是要求有多少對\(l\le r\)
程式碼:\(O(2^nn)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 20 #define ull unsigned long long #define Rd() (1ull*rand()*rand()*rand()*rand()+1) using namespace std; int n;struct Hash { ull x,y;I Hash(Con ull& a=0,Con ull& b=0):x(a),y(b){} I Hash operator ^ (Con Hash& o) Con {return Hash(x^o.x,y^o.y);} I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;} }a[1<<N],h[1<<N];map<Hash,int> S[4]; namespace FastIO { #define FS 100000 #define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++) char oc,FI[FS],*FA=FI,*FB=FI; Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));} Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);} }using namespace FastIO; int main() { srand(469762049);if(read(n),n==1) return puts("2"),0;//特判n=1,由於必須交換,單獨的1是不合法的 RI i,x,l;for(l=1<<n,i=1;i<=l;++i) read(x),a[i]=h[x^(l-1)].x?h[x^(l-1)]:(h[x]=Hash(Rd(),Rd()));//給能配對的兩個數隨機同一個權值 ull ans=0;for(S[0][Hash()]=i=1;i<=l;++i) ans+=S[i%4][a[i]=a[i]^a[i-1]]++;return printf("%llu\n",1LL*l*(l+1)/2-ans),0;//利用map求解非法方案數 }