Newcoder 26 B.点权和(机智)

186 篇文章 0 订阅

Description

给你一棵树,最开始点权为 0 0 0,每次将与一个点 x x x树上距离 ≤ 1 \le 1 1的所有点点权 + 1 +1 +1,之后询问这些点修改后的点权和.

Input

第一行两个数 n n n m m m

第二行 n − 1 n-1 n1个数,第 i i i个数 f a [ i + 1 ] fa[i + 1] fa[i+1]表示 i + 1 i + 1 i+1点的父亲编号,保证 f a [ i + 1 ] &lt; i + 1 fa[i + 1]&lt;i + 1 fa[i+1]<i+1

第三行 m m m个数,每个数 x x x次表示这次操作的点是 x x x

( n ≤ 1 0 5 , m ≤ 1 0 6 ) (n\le 10^5,m\le 10^6) (n105,m106)

Output

输出一个数,即这 m m m次操作的答案的 h a s h hash hash

如果是第 i i i次操作,这次操作结果为 a n s ans ans,则这个 h a s h hash hash值加上 i ⋅ a n s i\cdot ans ians

输出 h a s h hash hash值对 19260817 19260817 19260817取模的结果

Sample Input

6 3
1 1 2 3 3
1 2 3

Sample Output

34

Solution

查询一点 u u u的答案分为以下

1.由于 u u u被操作 c n t [ u ] cnt[u] cnt[u]次使得 u u u以及 u u u的所有邻接点答案加一,该部分贡献为 c n t [ u ] ⋅ ( d u [ u ] + 1 ) cnt[u]\cdot (du[u]+1) cnt[u](du[u]+1)

2.由于 u u u的父亲被操作 c n t [ f a [ u ] ] cnt[fa[u]] cnt[fa[u]]次,使得 u u u f a [ u ] fa[u] fa[u]答案都加一,该部分贡献为 2 ⋅ c n t [ f a [ u ] ] 2\cdot cnt[fa[u]] 2cnt[fa[u]]

3.由于 u u u的爷爷被操作 c n t [ f a [ f a [ u ] ] ] cnt[fa[fa[u]]] cnt[fa[fa[u]]]次,使得 f a [ u ] fa[u] fa[u]答案加一,该部分贡献为 c n t [ f a [ f a [ u ] ] ] cnt[fa[fa[u]]] cnt[fa[fa[u]]]

4.由于 u u u的兄弟被操作 s c n t [ f a [ u ] ] − c n t [ u ] scnt[fa[u]]-cnt[u] scnt[fa[u]]cnt[u]次,使得 f a [ u ] fa[u] fa[u]答案加上该部分,该部分贡献 s c n t [ f a [ u ] ] − c n t [ u ] scnt[fa[u]]-cnt[u] scnt[fa[u]]cnt[u],其中 s c n t [ f a [ u ] ] scnt[fa[u]] scnt[fa[u]]表示 f a [ u ] fa[u] fa[u]的儿子们被操作的次数之和

5.由于 u u u的儿子被操作使得 u u u的答案加上对应值,此部分可以在其儿子被操作时直接累加到 u u u的答案上

u u u点操作一次的影响为

1.对 u u u父亲的影响,使得 f a [ u ] fa[u] fa[u]的答案加二

2.对 u u u爷爷的影响,使得 f a [ f a [ u ] ] fa[fa[u]] fa[fa[u]]的答案加一

故只需要维护 c n t [ u ] , s c n t [ u ] cnt[u],scnt[u] cnt[u],scnt[u]以及统计答案的 a n s [ u ] ans[u] ans[u]即可,时间复杂度 O ( n + m ) O(n+m) O(n+m)

Code

#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=100005;
#define mod 19260817
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
int n,m,f[maxn],du[maxn],cnt[maxn],scnt[maxn],ans[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++)scanf("%d",&f[i]),du[i]++,du[f[i]]++;
	int sum=0;
	for(int i=1;i<=m;i++)
	{
		int u,res=0;
		scanf("%d",&u);
		ans[u]=add(ans[u],du[u]+1);
		if(f[u])ans[f[u]]=add(ans[f[u]],2),scnt[f[u]]=add(scnt[f[u]],1);
		if(f[f[u]])ans[f[f[u]]]=add(ans[f[f[u]]],1);
		cnt[u]=add(cnt[u],1);
		if(f[u])
		{
			res=add(res,mul(2,cnt[f[u]]));
			res=add(res,add(scnt[f[u]],mod-cnt[u]));
		}
		if(f[f[u]])res=add(res,cnt[f[f[u]]]);
		res=add(res,ans[u]);
		sum=add(sum,mul(i,res)); 
	}
	printf("%d\n",sum);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值