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;
}