来源:牛客网:

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

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

输入描述:
第一行两个数n和m
第二行n-1个数,第i个数fa[i + 1]表示i + 1点的父亲编号,保证fa[i + 1]<i + 1
第三行m个数,每个数x依次表示这次操作的点是x
输出描述:
输出一个数,即这m次操作的答案的hash值
如果是第i次操作,这次操作结果为ans,则这个hash值加上
i * ans
输出hash值对19260817取模的结果
示例1
输入
复制

6 3
1 1 2 3 3
1 2 3

输出
复制

34

示例2
输入
复制

6 10
1 1 2 3 3
1 4 6 5 2 3 3 3 3 3

输出
复制

869

备注:
n <= 100000
m <= 10000000

题解:

吐槽一下,题意说的不明确,搞的我一阵子以为自己读错了
hash值+=i*ans,而ans等于点权+1后,与点x树上距离<=1的所有点权和
样例中最后答案就是:3 * 1+5 * 2+7 * 3=34
根据题意,当影响一个点x后,x本身,x的父亲,x的儿子,都会受到影响
int fa[x];//父亲节点
int now[x],son[x],grandson[x];//分别表示当前点被操作次数,当前点的孩子节点操作次数,当前点的孙子节点操作次数
int kid[x];//x的儿子节点有多少个

为啥要用当前点的儿子和孙子节点,我是这么理解的,因为每个点的父亲节点是唯一的,一旦确定点x,其父亲节点,祖父节点都可以确定,而一个点有未知个儿子节点,所以用kid来记录儿子节点的数量。

最后计算x的贡献其实是x本身,x的父亲,x的儿子三个贡献和:
x的父亲:now[fa[x]]+son[fa[x]]+now[fa[fa[x]]]
x的父亲节点的点权受x的父亲节点本身,x节点以及x的父亲的父亲节点
x本身:now[fa[x]]+now[x]+son[x]
x的孩子:kid[x]*now[x]+son[x]+grandson[x]

代码:

代码只过了70%,我也没找到哪里错了,愁。。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxn 10000008
const int mod=19260817;
typedef long long ll; 
using namespace std;
int fa[maxn];
int now[maxn],son[maxn],grandson[maxn];
int kid[maxn];
int main(){
   
	int n,m;
	cin>>n>>m;
	for(int i=2;i<=n;i++)
	{
   
		int x;
		cin>>x;
		fa[i]=x;
		kid[x]++;
	}
	ll sum=0;
	for(int i=1;i<=m;i++)
	{
   
		int x;
		cin>>x;
		now[x]=(now[x]+1)%mod;
		if(fa[x])son[fa[x]]++;
		if(fa[fa[x]])grandson[fa[fa[x]]]++;
		int f=(now[fa[x]]+son[fa[x]]+now[fa[fa[x]]])%mod;
		int m=(now[fa[x]]+now[x]+son[x])%mod;
		int s=(kid[x]*now[x]%mod+son[x]+grandson[x])%mod;
		ll ans=(f+m+s)%mod;
		sum=(sum+ans*i)%mod;
		
	} 
	cout<<sum%mod;
	return 0;
}