赤の夜

赤の夜

【问题描述】
数据结构大师 ddd 给你出了一道题:
给你一棵树,最开始点权为
这些点修改后的点权和。
【输入】
第一行两个数 n 和 m;
之后 n-1 行,第 i 行一个数 fa[i]
之后 m 行每行一个数 x 表示这次操作的点是x。
【输出】
输出一个数,即这 m 次询问的答案的和
保证答案在有符号 64 位整数范围内
输入样例#1:
6 3
1 1 2 3 3
1 2 3
输出样例#1:
15
输入样例#2:
6 10
1 1 2 3 3
1 4 6 5 2 3 3 3 3 3
输出样例#2
115

数据范围
n <= 100000 m <= 10000000


题解
小题其实并不难,千万别跟着题目的意思去模拟。其实,对于每一个点来说,只要它本身或是周围有点被选中,那么就会造成一次价值(第 i 次记作 Vi)。实际上 Vi 就等于 i 。如果把这个点每次的代价都写出来,很明显是一个等差数列,只要统计出总个数就可以直接得出这个点上形成的总价值。
所以我们会想到统计出每一个点被点的次数,然后直接刷答案。总复杂度为 O(2N2)

代码

#include
#include
#include
#include
#define Q(ret) (ret<<3)+(ret<<1)
using namespace std;

const int maxn=100005;
int n,m,tot,f[maxn],lnk[maxn],nxt[maxn<<1],son[maxn<<1];
long long ans;
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=Q(ret)+ch-'0',ch=getchar();
    return ret*f;
}
void add(int x,int y){son[++tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;}
long long worked(int x)
{
    long long s=f[x];for (int i=lnk[x];i;i=nxt[i]) s+=f[son[i]];
    if (s&1) return (s+1>>1)*s;
    else return (s>>1)*(s+1);
}
int main()
{
    freopen("chiye.in","r",stdin);
    freopen("chiye.out","w",stdout);
    n=read();m=read();
    for (int i=2;i<=n;i++)
    {
        int x=read();
        add(i,x);add(x,i);
    }
    for (int i=1;i<=m;i++) f[read()]++;
    for (int i=1;i<=n;i++) ans+=worked(i);
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(2018)