【题解】 CF1418G Three Occurrences hash+双指针+差分
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;
}