题解 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;
}
posted @ 2021-11-19 15:05  Administrator-09  阅读(1)  评论(0编辑  收藏  举报