2019西安邀请赛 And And And 树形dp

题目链接:https://nanti.jisuanke.com/t/39277

题意:

2019西安邀请赛 And And And 树形dp_第1张图片

题解:就是看对于一对(u,v),满足路径上权值异或和为0,是多少路径的子集,然后队友所有加和

题解:首先对于我们先处理出从根节点异或下来的值,若(u,v)得到的结果一样,那么u到v的异或和为0,若u,v某一个是另一个的父辈,那么这一对贡献值即为孩子这一分支节点的数目*父辈除去这一分支几点数目剩余的,否则,贡献值为两个分支节点数目相乘,我是分两次计算的,第一次计算保存的都是每一分支节点的数目,这样对于父子这一条链的是不对的,那么我又计算一次,把父子这样的,减去这一分支加上n-父亲这一分支的数目。


#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
const ll mod=1e9+7;
struct node{
	int to,nex;
	ll d;
}e[N*2];
int head[N],len;
int n;
map mp;
void add(int x,int y,ll z)
{
	e[len].to=y;
	e[len].d=z;
	e[len].nex=head[x];
	head[x]=len++;
}
ll son[N],v[N];
void dfs1(int u,int fa,ll val)
{
	int to;
	ll tval;
	son[u]=1;
	v[u]=val;
	for(int i=head[u];i!=-1;i=e[i].nex)
	{
		to=e[i].to;
		if(to==fa) continue;
		tval=val^e[i].d;
		dfs1(to,u,tval);
		son[u]+=son[to];
	}
}
ll ans;
void dfs2(int u,int fa)
{
	int to;
	ans=(ans+son[u]*mp[v[u]]%mod)%mod;
	mp[v[u]]=(mp[v[u]]+son[u])%mod;
	for(int i=head[u];i!=-1;i=e[i].nex)
	{
		to=e[i].to;
		if(to==fa) continue;
		dfs2(to,u);
	}
}
void dfs3(int u,int fa)
{
	int to;
	ans=((ans+son[u]*mp[v[u]]%mod)%mod+mod)%mod;
	for(int i=head[u];i!=-1;i=e[i].nex)
	{
		to=e[i].to;
		if(to==fa) continue;
		mp[v[u]]=mp[v[u]]-son[to]+n-son[u];
		dfs3(to,u);
		mp[v[u]]=mp[v[u]]+son[to]-n+son[u];
	}
}
int main()
{
	int x;
	ll y;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) head[i]=-1;
	for(int i=2;i<=n;i++)
	{
		scanf("%d%lld",&x,&y);
		add(x,i,y);
		add(i,x,y);
	}
	
	dfs1(1,0,0);
	dfs2(1,0);  // 都保存每一分支的节点数目 
	mp.clear();
	dfs3(1,0);  // 处理父子一条链的情况 
	printf("%lld\n",ans);
	return 0;
}
/*
7
1 0
1 1
2 1
2 100
3 200
3 3000000000000
*/

 

你可能感兴趣的:(树形dp)