NC14393 点权和(思维)

这种题肯定不会是暴力枚举,多半考虑是贡献

首先我们要想清楚的是,我每次操作,会对哪些节点产生影响,答案又是从哪些节点更新而来

很显然我们会从儿子,自身,父亲这三个角度去思考问题。

所以我们会设计状态 now[]表示自身被操作的次数,a[],表示被儿子影响的次数,b[]表示的是被孙子操作的次数,in[]表示的是儿子个数。

为什么这里会出现孙子呢,因为孙子+1,儿子+1,那么当答案的时候,儿子的值变化了,所以答案也变化了。

为什么不用专门设计父亲呢?因为所有的父亲都是别人的儿子,所以这样的状态足以,不然就更加复杂了。

假如我们对当前点进行更新,那么我们可以更新:

当前点的权值,别的点的儿子影响,孙子影响。

具体解释看代码,很多不同的答案都是对的,只要能表达清楚就行

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int mod=19260817;
ll p[N],a[N],b[N];
ll in[N];
ll now[N];
int main(){
    int n,m;
    cin>>n>>m;
    int i;
    for(i=2;i<=n;i++){
        int x;
        scanf("%d",&x);
        in[x]++;
        p[i]=x;
    }
    ll res=0;
    for(i=1;i<=m;i++){
        int x;
        scanf("%d",&x);
        now[x]++;//当前操作+1
        ll ans=0;
        if(p[x]){
            ll t=p[x];
            a[t]++;//父亲被自己影响+1
            ans=(ans+now[t]*2ll+a[t])%mod;//父亲的操作要×2,因为父亲和自己都加了1,再加上父亲被自己影响的次数
        }
        if(p[p[x]]){
            ll t=p[p[x]];
            ans=(ans+now[t])%mod;//爷爷的操作影响
            b[t]++;//爷爷被自己的影响
        }
        ans=(ans+(in[x]+1)%mod*now[x]%mod+a[x]*2+b[x])%mod;//自己操作数的贡献,以及儿子对儿子和自己的贡献以及孙子的贡献
        res=(res+ans*i+mod)%mod;
    }
    cout<<res<<endl;
    return 0;
}
View Code

 

posted @ 2020-04-12 09:42  朝暮不思  阅读(157)  评论(0编辑  收藏  举报