HDU 5416 CRB and Tree (树形dp)

题意:给定带权边的树,求树上异或和为 k k k的路径数(无序对), f ( u , u ) = 0 f(u,u)=0 f(u,u)=0

思路:树形 d p dp dp,先 d f s dfs dfs求出所有从1开始出发到 u u u的异或和 d p [ u ] dp[u] dp[u],由异或和性质,显然有 f ( u , v ) = d p [ u ] ⊕ d p [ v ] f(u,v)=dp[u]\oplus dp[v] f(u,v)=dp[u]dp[v]

统计答案时:对于结点 u u u,我们只需求出 c n t [ d p [ u ] ⊕ k ] cnt[dp[u]\oplus k] cnt[dp[u]k]的个数即可,因为是无序,最后需要除以2,而 k = 0 k=0 k=0时自己只会被算一次,所以要特判下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int T,n,q,cnt,h[N];
struct edge{
	int to,nt,w;
}e[N<<1];
void add(int u,int v,int w){
	e[++cnt]={v,h[u],w},h[u]=cnt;
	e[++cnt]={u,h[v],w},h[v]=cnt;
} 
ll dp[N];
int mp[N];
void dfs(int u,int fa){
	for(int i=h[u];i;i=e[i].nt){
		int v=e[i].to,w=e[i].w;
		if(v==fa) continue;
		dp[v]=w^dp[u];
		dfs(v,u);
	}mp[dp[u]]++;
}
int main(){
	scanf("%d",&T);
	while(T--){
		cnt=0,mst(h,0),dp[1]=0,mst(mp,0);
		scanf("%d",&n);for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w);
		dfs(1,0); 	
		scanf("%d",&q);
		while(q--){
			int s;scanf("%d",&s);
			ll ans=0;
		for(int i=1;i<=n;i++) ans+=mp[dp[i]^s];
		if(!s) ans+=n;//当s=0时  (u,u)只算了一次,其他的算了两次,所以要加n 使(u,u)也被算两次 
		ans>>=1;//最后除以2就是无序对. 
		printf("%lld\n",ans); 
		}
	}
	return 0;
}