1. POI2015 - Podział naszyjnika
考虑对每个位置附一个随机权值,保证序列中所有等于某个数的位置权值异或和为 \(0\)。则一种划分合法当且仅当两个区间异或和都为 \(0\),相当于找到一个区间 \([L,R]\) 异或和为 \(0\)。于是用 umap 记录前缀异或和即可。第二问把每个相同的前缀异或和放到一个 vector 里双指针即可。注意 \([1,i],[i+1,n]\) 之类的划分可能会被统计两次,解决方法是不统计所有以 \(n\) 结尾的区间。
点击查看代码
//P3587
#include <bits/stdc++.h>
using namespace std; typedef long long ll;
void solve();int main(){ solve(); return 0; }
const int N = 1e6 + 10;
int n, k, a[N], cnt, mx;
ll xom[N], val[N], ans;
unordered_map<ll, int> mp;
vector<int> g[N];
int cl(int x, int y){
return min(y - x, x + n - y);
}
mt19937 rng(time(0));
uniform_int_distribution<long long>gen(1,0x3f3f3f3f3f3f3f3f);
void solve(){
srand(unsigned(time(NULL)));
mp[0] = ++ cnt;
g[mp[0]].push_back(0);
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
val[i] = gen(rng);
xom[a[i]] ^= val[i];
}
for(int i = 1; i <= n; ++ i){
val[i] ^= xom[a[i]];
xom[a[i]] = 0;
val[i] ^= val[i-1];
if(!mp[val[i]]){
mp[val[i]] = ++ cnt;
}
g[mp[val[i]]].push_back(i);
}
for(int i = 1; i <= cnt; ++ i){
int x = g[i].size();
if(mp[val[n]] == i){
-- x;
}
ans += (ll)x * (x-1) / 2;
int pr = 0;
for(int j = 1; j < x; ++ j){
while(pr < j && cl(g[i][pr], g[i][j]) < cl(g[i][pr+1], g[i][j])){
++ pr;
}
mx = max(mx, cl(g[i][pr], g[i][j]));
}
}
printf("%lld %d", ans, n - mx - mx);
}