题解 dojave
- 给定长度1e7的序列和1e7次查询,每次查询一个区间是不是所有数都出现了偶数次:
一个做法是将每个数映射到一个大随机数,然后每次查询区间异或和是不是0
令区间异或和为 \(t\),令 \(n=2^m-1\)
于是一个暴力是枚举区间内的每个元素 \(x\),检查 \(x\oplus(t\oplus n)\) 是不是在当前区间内
这个不好做,考虑正难则反,统计不合法区间数
- 有些求合法数量的题可以考虑正难则反,求不合法区间数然后减去
于是考虑一个不合法区间是什么样子
要求与每个元素配对的元素都在这个区间内
那区间长必须是偶数
令这对元素为 \(x, y\)
则有 \(x\oplus (t\oplus n)=y\),所以 \(x\oplus y=t\oplus n\)
然后区间异或和等于 \(t\),推一下可以得到区间长度是4的倍数
于是变成了统计有多少区间满足区间内每个数都是成对出现的
可以将每对数映射到一个大随机数后check区间异或和是否为0
注意区间长的限制,可以对每个点按下标模4的值开map记录异或和
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 2000010
#define ll long long
// #define int long long
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int a[N];
namespace force{
bool vis[N];
bool check(int l, int r) {
// cout<<"check: "<<l<<' '<<r<<endl;
for (int i=0; i<n; ++i) vis[i]=0;
int sum=0;
for (int i=l; i<=r; ++i) sum^=a[i], vis[a[i]]=1;
if (sum==(1<<m)-1) return 1;
int need=sum^((1<<m)-1);
for (int i=l; i<=r; ++i) if (!vis[a[i]^need]) return 1;
return 0;
}
void solve() {
int ans=0;
for (int i=1; i<=n; ++i) for (int j=i; j<=n; ++j) {
if (check(i, j)) ++ans; //, cout<<"return 1"<<endl;
// else cout<<"return 0"<<endl;
}
printf("%d\n", ans);
exit(0);
}
}
namespace task{
ll val[N];
int rk[N];
bool vis[N];
unordered_map<ll, int> mp[4];
void solve() {
random_device(seed);
srand(seed());
int lim=(1<<m)-1;
for (int i=1; i<=n; ++i) rk[a[i]]=i;
for (int i=1; i<=n; ++i) if (!vis[i]) {
vis[i]=vis[rk[lim^a[i]]]=1;
val[i]=val[rk[lim^a[i]]]=1ll*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand();
}
ll pre=0, ans=0;
mp[0][0]=1;
// cout<<"val: "; for (int i=1; i<=n; ++i) cout<<val[i]<<' '; cout<<endl;
for (int i=1; i<=n; ++i) {
pre^=val[i];
if (mp[i%4].find(pre)!=mp[i%4].end()) ans+=mp[i%4][pre];
++mp[i%4][pre];
}
printf("%lld\n", 1ll*n*(n+1)/2-ans);
exit(0);
}
}
signed main()
{
freopen("dojave.in", "r", stdin);
freopen("dojave.out", "w", stdout);
m=read(); n=1<<m;
if (m==1) {puts("2"); return 0;}
for (int i=1; i<=n; ++i) a[i]=read();
// force::solve();
task::solve();
return 0;
}