【題解】 CF1418G Three Occurrences hash+雙指標+差分
阿新 • • 發佈:2020-09-20
Legend
Link \(\textrm{to Codeforces}\)。
給定長度為 \(n\ (1 \le n \le 5\times 10^5)\) 的序列,一個序列稱作是好的當且僅當序列中所有元素出現恰好 \(3\) 次。
請求出這個序列有多少個連續子序列是好的。
Editorial
做資料結構時候做到的。。。以為是什麼神仙資料結構。。。結果看了下 \(\rm{tag:hash}\)。
注意題目中的條件,數字出現恰好 \(3\) 次。可以把同樣的數字按順序給定 \(3\) 個雜湊值 \(v_1,v_2,v_3\),(每個數字的雜湊值不同)
並且滿足 \(v_1 \operatorname{xor} v_2 \operatorname{xor} v_3 = 0\)
這樣子做只要一個區間異或和為 \(0\) 就說明有很大概率說明所有數字出現的次數都是 \(3\) 的倍數。
我們只要通過雙指標卡著區間就可以確保每個數字出現次數都是 \(3\) 次以內。hash 加差分應該是個套路不贅述。
複雜度 \(O(n \log n)\)。
Code
在 \(\rm{Codeforces}\),單雜湊會把你卡到生不如死。
所以還是老老實實寫雙雜湊吧!
#include <bits/stdc++.h> #define ULL unsigned long long #define mp make_pair #define p pair<ULL ,ULL> using namespace std; map<p ,int> MAP; int read(){ char k = getchar(); int x = 0; while(k < '0' || k > '9') k = getchar(); while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar(); return x; } const int MX = 5e5 + 233; int app[MX] ,a[MX]; ULL rnd[3][MX] ,cnt[MX] ,ha[MX] ,ha2[MX]; ULL rnd2[3][MX]; ULL r(){ return ((ULL)(rand()) << 31) ^ rand(); } int main(){ srand(19260817); int n = read(); for(int i = 1 ; i < MX ; ++i){ rnd[0][i] = r(); rnd[1][i] = r(); rnd[2][i] = rnd[0][i] ^ rnd[1][i]; rnd2[0][i] = r(); rnd2[1][i] = r(); rnd2[2][i] = rnd2[0][i] ^ rnd2[1][i]; } int l = 1; ULL Ans = 0; MAP[mp(0ull ,0ull)] = 1; app[0] = 1; for(int i = 1 ; i <= n ; ++i){ a[i] = read(); ha[i] = ha[i - 1] ^ rnd[cnt[a[i]]][a[i]]; ha2[i] = ha2[i - 1] ^ rnd2[cnt[a[i]]][a[i]]; cnt[a[i]] = (cnt[a[i]] + 1) % 3; while(app[a[i]] == 3){ --app[a[l]]; --MAP[mp(ha[l - 1] ,ha2[l - 1])]; ++l; } ++app[a[i]]; Ans += MAP[mp(ha[i] ,ha2[i])]; // fprintf(stderr ,"i = %d ,Ans = %llu\n" ,i ,Ans); ++MAP[mp(ha[i] ,ha2[i])]; } cout << Ans << endl; return 0; }