ABC201E Xor Distances 題解

Sorato發表於2024-08-10

ABC201E Xor Distances 題解

題目大意

給定一個帶權樹,求樹上每兩點的簡單路徑上的邊權的異或和的和。

形式化的,定義 \(dis(i,j)\)\(i\)\(j\) 的簡單路徑上的邊權的異或和,求 \(\large\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\text{dis}(i,j)\)

Solve

\(\large f(u)=\sum\limits_{i=1}^n\text{dis}(u,i)\)

指定 \(1\) 為根,考慮先 dfs 遍歷樹求出 \(f(1)\),然後換根 \(\text{DP}\)

若已知 \(f(u)\),對於 \(v\in son_u\),我們分兩部分考慮,即在子樹 \(v\) 中的 \(A\) 集合和不在子樹 \(v\) 中的 \(B\) 集合。

  • 對於 \(B\) 中的點,根從 \(u\) 轉移到 \(v\),他們到根的路徑的異或和會多異或上 \(w(u,v)\)
  • 對於 \(A\) 中的店,他們到根的路徑的異或和會少異或上 \(w(u,v)\),由於異或的自反性,透過再異或上一個 \(w(u,v)\) 也可以實現。

綜上,\(f(v)\) 即為 \(f(u)\) 的每一項異或上 \(w(u,v)\) 的和,即 \(\large f(v)=\sum\limits_{i=1}^n dis(u,i)\oplus w(u,v)\)

考慮如何計算。

不難想到:用 \(cnt\) 記錄下 \(f(u)\) 的每一項 \(x\) 二進位制下 \(1\)\(0\) 的個數,按位列舉 \(w(u,v)\),若其第 \(i\) 位為 \(1\),則 \(x\oplus w(u,v)\) 後第 \(i\) 位會與原來相反,所以此時交換 \(cnt_{i,1}\)\(cnt_{i,0}\)

經過交換後,我們有 \(\large f(v)=\sum\limits_{i=0}^{m-1}2^i\times cnt_{i,1}\)。本題 \(w\leq2^{60}\),故 \(m=60\)

答案即為 \(\large\frac {\sum\limits_{u=1}^nf(u)} 2\)

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	short f=1;
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c>='0'&&c<='9')	x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
#define mod 1000000007
int n,cnt[60][2],ans,mi[60]={1};
#define PII pair<int,int>
vector<PII>e[200010];
void dfs1(int u,int d,int fa)
{
	for(int i=59;i>=0;i--)	cnt[i][d>>i&1]++;
	for(auto i:e[u])
		if(i.first!=fa)	dfs1(i.first,d^i.second,u);
}
void dfs2(int u,int fa)
{
	for(int i=59;i>=0;i--)	ans=(ans+mi[i]*cnt[i][1]%mod)%mod;
	for(auto i:e[u])
	if(i.first!=fa)
	{
		for(int j=59;j>=0;j--)
			if(i.second>>j&1)
				swap(cnt[j][1],cnt[j][0]);
		dfs2(i.first,u);
		for(int j=59;j>=0;j--)
			if(i.second>>j&1)
				swap(cnt[j][1],cnt[j][0]);
	}
}
signed main()
{
	n=read();
	for(int i=1;i<60;i=-~i)	mi[i]=(mi[i-1]<<1)%mod;
	for(int i=1,u,v,w;i<n;i=-~i)
		u=read(),v=read(),w=read(),
		e[u].push_back({v,w}),e[v].push_back({u,w});
	dfs1(1,0,0);dfs2(1,0);
	return printf("%lld",ans*500000004/*顯然這是2在mod 1e9+7下的逆元*/%mod),0;
}

相關文章