点权和(树形结构思维)
题意:
给你一棵树,最开始点权为0,每次将与一个点x树上距离<=1的所有点点权+1,之后询问这些点修改后的点权和.输出一个数,即这m次操作的答案的hash值。如果是第i次操作,这次操作结果为ans,则这个hash值加上i * ans输出hash值对19260817取模的结果
n <= 100000
m <= 10000000
思路:
因为m很大所以,又在线,所以需要O(1)处理每次。那么这题的突破口就是树上距离小于等于1的点,可以从每个点影响周围点的贡献入手。 一个点无非就是影响他的儿子,他的父亲,他自己。
所以只要把 这个点父亲节点的贡献+这个点儿子节点的贡献+这个点本身的贡献 就完事了
定义数组 now[x] 表示节点x被操作的次数
son[x] 表示节点x的儿子被操作的次数
b[x] 表示节点x的孙子被操作的次数 为什么要孙子,因为在处理儿子节点的贡献时,孙子节点对儿子节点会产生影响的。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod=19260817;
const int N=1e5+5;
int n,m,fa[N];//表示当前节点的父亲节点是谁
ll siz[N];//表示当前节点儿子的数量
ll son[N];//表示儿子节点被操作的次数
ll sson[N];//表示孙子节点被操作的次数
ll now[N];//表示当前节点被操作的次数
ll ans;
int main(){
scanf("%d%d",&n,&m);
for(int i=2,x;i<=n;i++){
scanf("%d",&x);
fa[i]=x;
siz[x]++;
}
for(int i=1,x;i<=m;i++){
scanf("%d",&x);
now[x]++;//更新
if(fa[x])son[fa[x]]++;
if(fa[fa[x]])sson[fa[fa[x]]]++;
ll k=0;
k=(now[x]+now[fa[x]])%mod+son[x];//自己的贡献
k%=mod;
k+=(now[fa[x]]+son[fa[x]]+now[fa[fa[x]]])%mod;//父亲的贡献
k%=mod;
k+=(siz[x]*now[x]%mod+son[x]+sson[x])%mod;//所有儿子的贡献
k%=mod;
ans=(ans+k*i%mod)%mod;
}
printf("%lld",ans);
}