题解 异或

传送门

刚了四个小时并且没有任何用处

一开始尝试按位拆开考虑贡献,但发现极难DP
想了trie树但感觉trie树只能处理两个数的相对关系,于是就没细想
然后正解扔到trie树上了
思路是枚举 \(k\),用trie树处理 \(i\)\(j\)
发现比较大小时我们只需要考虑两者的最高不同位

  • 两个数比较大小时,在trie树上的表现是会在最高不同位分到左右儿子,可以借助这个性质完成一些计数

于是考虑一个 \(a_i\) 一定会在某个地方成为 \(a_k\) 在trie树上的兄弟
此时分类讨论得 \(a_j\) 的这一位一定是和 \(a_i\) 的这一位是一样的
于是若 \(a_j\)\(a_k\) 兄弟的子树中,\(i<j\) 的有 \(\frac{siz*(siz-1)}{2}\)
然后考虑子树外,贡献为 子树外这一位为0/1的数的个数×子树大小
然后怎么保证 \(i<j\) 呢?可以在插入每个i的时候统计已经在子树外的合法数个数,减去即可

  • 所以对于三元组 \((i, j, k)\) 满足 \(i<j<k\) 及一些其它性质的计数,若枚举其中一个,对剩下两个计数
    也许可以在枚举到剩下两个中较先枚举到的那个时统计一下不满足顺序关系的另一个的个数,方便以后减去
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
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;
int a[N];

namespace force{
	int buc[2010][2010], siz[N];
	ll ans;
	void solve() {
		for (int i=n; i; --i) {
			for (int j=i+1; j<=n; ++j) buc[i][++siz[i]]=a[i]^a[j];
			sort(buc[i]+1, buc[i]+siz[i]+1);
			for (int j=i+1; j<=n; ++j) {
				ans+=siz[j]-(upper_bound(buc[j]+1, buc[j]+siz[j]+1, a[i]^a[j])-buc[j])+1;
			}
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task1{
	int suf[70], tem[70];
	ll ans, tab[70], pre[70];
	void solve() {
		for (int i=n; i; --i) {
			ans+=tab[a[i]];
			memset(tem, 0, sizeof(tem));
			memset(pre, 0, sizeof(pre));
			for (int j=0; j<70; ++j) tem[a[i]^j]+=suf[j];
			pre[0]=tem[69];
			for (int j=68; ~j; --j) pre[j]=pre[j+1]+tem[j];
			for (int j=0; j<70; ++j) tab[j]+=pre[(j^a[i])+1];
			++suf[a[i]];
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task{
	int siz[N*35], son[N*35][2], tot, now;
	int cnt[50][2];
	ll sum[N*35], ans;
	#define siz(p) siz[p]
	#define son(a, b) son[a][b]
	#define sum(a) sum[a]
	void ins(int dat) {
		int p=0, *t;
		for (int i=30; ~i; --i,p=*t) {
			int s=dat&(1<<i)?1:0;
			t=&son(p, s);
			if (!(*t)) *t=++tot;
			sum(*t)+=cnt[i][s]-siz(*t);
			++siz[*t]; ++cnt[i][s];
		}
	}
	ll query(int dat) {
		// cout<<"query: "<<dat<<endl;
		int p=0, *t;
		ll ans=0;
		for (int i=30; ~i; --i,p=*t) {
			int s=dat&(1<<i)?1:0;
			t=&son(p, s);
			if (son(p, s^1)) {
				ans+=1ll*siz(son(p, s^1))*(siz(son(p, s^1))-1)/2;
				ans+=1ll*(cnt[i][s^1]-siz(son(p, s^1)))*siz(son(p, s^1))-sum(son(p, s^1));
				// cout<<"i: "<<i<<' '<<s<<endl;
				// cout<<cnt[i][s^1]<<' '<<siz(son(p, s^1))<<' '<<sum(son(p, s^1))<<endl;
			}
			if (!(*t)) break;
		}
		return ans;
	}
	void solve() {
		for (now=1; now<=n; ++now) {
			ans+=query(a[now]);
			ins(a[now]);
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("xor.in", "r", stdin);
	freopen("xor.out", "w", stdout);
	
	n=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	// if (n<=2000) force::solve();
	// else task1::solve();
	task::solve();

	return 0;
}
posted @ 2021-11-14 07:28  Administrator-09  阅读(2)  评论(0编辑  收藏  举报