luogu 模拟题 赤夜

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

pushup[x]+=siz[x] p u s h u p [ x ] + = s i z [ x ]

tag[x]++,sontag[f[x]]++ t a g [ x ] + + , s o n t a g [ f [ x ] ] + +

pushup[f[x]]+=2(xf[x]) p u s h u p [ f [ x ] ] + = 2 ( x 和 f [ x ] 都 会 加 一 )

pushup[f[f[x]]]+=1 p u s h u p [ f [ f [ x ] ] ] + = 1

查询时:
ans+=pushup[x]+2tag[f[x]]+tag[f[f[x]]]+sontag[f[x]]tag[x] a n s + = p u s h u p [ x ] + 2 ∗ t a g [ f [ x ] ] + t a g [ f [ f [ x ] ] ] + s o n t a g [ f [ x ] ] − t a g [ x ]

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

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

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

#include
#include
#include
#include
#include
#define LL long long
using 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;
}

你可能感兴趣的:(分析,优化,树)