luogu 模拟题 赤夜

来源:互联网 发布:大连东芝医疗知乎 编辑:程序博客网 时间:2024/05/19 14:01

这里写图片描述

做法一:对于每一个点的修改,顺序改变一下是不会影响结果的。我们离线做,可以一个点一个点的修改。
(还是过不了啊,仍然T)qwq。
做法二:
我们尽量把实际的操作搞成标记,不操作,以降低复杂度。
我们用三个数组实现。
pushup[x]表示x周围的点对x的影响,tag[x]记录的是x这个点操作的次数,sontag[x]表示的是x所有的儿子节点的操作次数。
那么修改时:

pushup[x]+=siz[x]

tag[x]++,sontag[f[x]]++

pushup[f[x]]+=2(xf[x])

pushup[f[f[x]]]+=1

查询时:
ans+=pushup[x]+2tag[f[x]]+tag[f[f[x]]]+sontag[f[x]]tag[x]

分别是自己上面操作时产生的和,父亲每操作一次自己的和就会+2(父亲和自己个占1),爷爷每操作一次自己会+1(只有父亲的1),加上父亲的其他儿子每操作一次就会使父亲+1,父亲又在x周围。

总的来说就是操作自己的贡献+操作其他点对自己产生的贡献。

时间复杂度O(n+m)。
需要读入优化。

#include<iostream>#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#define LL long longusing namespace std;const int N=1e5+77;int n,m,siz[N],f[N];LL ans,pushup[N],tag[N],sontag[N];long long read(){    long long res = 0;    char ch = 0;    while (ch < '0' || ch > '9')        ch = getchar();    while (ch >= '0' && ch <= '9')    {        res = res * 10 + ch - '0';        ch = getchar();    }    return res;}int main(){    freopen("sample.in","r",stdin);    freopen("sample.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) siz[i]++;    for(int x,i=2;i<=n;i++)    {        x=read();        f[i]=x;        siz[i]++;siz[x]++;    }    for(int x,i=1;i<=m;i++)    {        x=read();        pushup[x]+=siz[x];        pushup[f[x]]+=2;        pushup[f[f[x]]]+=1;        tag[x]++;        sontag[f[x]]++;        LL s=0;        s+=pushup[x]+2*tag[f[x]]+tag[f[f[x]]]+sontag[f[x]]-tag[x];        ans+=s;    }    printf("%lld\n",ans);    return 0;}
原创粉丝点击