1. 程式人生 > 其它 >2020CCPC長春F. Strange Memory

2020CCPC長春F. Strange Memory

題目大意

一棵以 \(1\) 為根的 \(n(2\leq n\leq 10^5)\) 的樹,每個節點 \(i\) 有權值 \(a_{i}(1\leq a_{i}\leq 10^6)\) ,求 \(\sum_{i=1}^{n}\sum_{j=i+1}^{n}[a_{i}\oplus a_{j}=a_{lca(i,j)}](i\oplus j)\)

思路

考慮 \(dsu\space on\space tree\) ,因為 \(a_{i}>0\) ,所以能夠產生貢獻的節點 \((i,j)\) 一定分屬 \(lca(i,j)\) 兩側,於是計算各個子樹的貢獻時,考慮到對於每個節點 \(x\) ,對其中一棵子樹中的節點 \(i\)

, 其他子樹中的每一個 \(a_{j} = a_{i}\oplus a_{x}\) 的節點 \(j\) 就會對答案產生 \(i\oplus j\) 的貢獻。所以我們可以用一個數組 \(f[a,b,c]\) 來記錄當權值為 \(a\) 時,該權值的節點編號在二進位制中第 \(b\) 為的數字為 \(c\) 的節點個數,然後我們就可以對 \(i\) 來按位列舉有多少 \(j\) 能夠對答案在這一位上產生貢獻來計算答案。我們先計算所有輕子樹內的答案,然後去掉輕子樹對 \(f\) 的貢獻,之後計算重子數的答案,之後保留其對 \(f\) 的貢獻,再遍歷所有子樹,計算 \(f\) 以及跨子樹的節點的答案,最後全部加起來即可。複雜度 \(O(nlogn)\)

程式碼

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

int N, A[maxn];
vector<int>G[maxn];
int vsize[maxn], hson[maxn], L[maxn], R[maxn], rnk[maxn], tot = 0;
LL tmp;
int f[1 << 20][20][2];

void add_edge(int from, int to)
{
	G[from].push_back(to);
	G[to].push_back(from);
}

void add(int v, int t)
{
	for (int i = 19; i >= 0; i--)
		f[A[v]][i][(v >> i) & 1] += t;
}

void dfs(int v,int p)
{
	hson[v] = 0;
	L[v] = ++tot;
	rnk[tot] = v;
	vsize[v] = 1;
	for (int i = 0; i < G[v].size(); i++)
	{
		int to = G[v][i];
		if (to == p)
			continue;
		dfs(to, v);
		vsize[v] += vsize[to];
		if (!hson[v] || vsize[to] > vsize[hson[v]])
			hson[v] = to;
	}
	R[v] = tot;
}

void dsu(int v, int p)
{
	for (int i = 0; i < G[v].size(); i++)
	{
		int to = G[v][i];
		if (to == p || to == hson[v])
			continue;
		dsu(to, v);//單個子樹內的貢獻
		for (int j = L[to]; j <= R[to]; j++)
			add(rnk[j], -1);//清空計數資訊
	}
	if (hson[v])
		dsu(hson[v], v);
	for (int i = 0; i < G[v].size(); i++)
	{
		int to = G[v][i];
		if (to == p || to == hson[v])
			continue;
		for (int j = L[to]; j <= R[to]; j++)
		{
			int tar = A[rnk[j]] ^ A[v];
			for (int i = 19; i >= 0; i--)
				tmp += (1LL << i) * (LL)f[tar][i][((rnk[j] >> i) & 1) ^ 1];
		}
		for (int j = L[to]; j <= R[to]; j++)
			add(rnk[j], 1);
	}
	add(v, 1);//加上自己的計數資訊
}

void solve()
{
	dfs(1, 0), dsu(1, 0);
	cout << tmp << endl;
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> A[i]; 
	int u, v;
	for (int i = 1; i < N; i++)
	{
		cin >> u >> v;
		add_edge(u, v);
	}
	solve();

	return 0;
}