2019西安邀请赛 J And And And

https://nanti.jisuanke.com/t/39277

上半年湘潭西安全败在树形DP不会,然后浪费大量时间后做另外一题还没做完,菜哭.jpg

这题首先是想到路径的异或为0可以变成点到根的路径异或值相等

然后想到枚举u' <v' 这个相等更方便,接着就是想怎么统计有多少u<v可以选

然后比赛的时候就不会了。。。。头都想烂了,主要是当时已经写了4题了,然后又到了后期,有点慌张,降智buff++,结果放弃这题去写一道原题了,结果那原题最后5分钟过了样例以后也WA了。

其实就按照dfs顺序,当前为异或值a[u]的点之前已经出现过异或值a[u]的点求总方案数就行了,这样不管是谁大谁小都是求了一次,不会造成重复。

那么对如果一个a[u]的祖先节点t也是a[u],那么方案数就是son[u]*(n-那个t到这个点u的儿子的v的son[v]),如果不是,那么就是son[t]*son[u],然后我们用一个map保存所有异或为a[u]的之前的节点要乘的总和及每次都ans+=son[u]*sum[a[u]],然后动态维护sum[a[u]]就行了。

#include<bits/stdc++.h>
#define maxl 200010
using namespace std;

const int mod=1e9+7;

int n,cnt=0;
int ehead[maxl],son[maxl];
long long ans;
long long a[maxl];
map<long long,long long>sum;
struct ed
{
	int to,nxt;
}e[maxl];

inline void add(int u,int v)
{
	e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
}

inline void dfs1(int u)
{
	int v;son[u]=1;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		dfs1(v);
		son[u]+=son[v];
	}
}

inline void prework()
{
	scanf("%d",&n);
	a[1]=0;cnt=0;
	int fa;long long w;
	for(int i=2;i<=n;i++)
	{
		scanf("%d%lld",&fa,&w);
		add(fa,i);a[i]=a[fa]^w;
	}
	dfs1(1);
}

inline void add(long long &x,long long y)
{
	x=((x+y)%mod+mod)%mod;
}

inline void dfs2(int u)
{
	int v;
	add(ans,1ll*sum[a[u]]*son[u]%mod);
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		sum[a[u]]=((sum[a[u]]+n-son[v])%mod+mod)%mod;
		dfs2(v);
		sum[a[u]]=((sum[a[u]]-(n-son[v]))%mod+mod)%mod;
	}
	sum[a[u]]=(sum[a[u]]+son[u])%mod;
}

inline void mainwork()
{
	ans=0;
	dfs2(1);
}

inline void print()
{
	printf("%lld",ans);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值