Wannafly挑战赛21-E:未来城市规划(树链剖分)

链接:https://www.nowcoder.com/acm/contest/159/E

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
乐乐做了个梦,梦见自己当了国王。乐乐王国有N座城市(编号从1~N,首都的编号为1)。乐乐王国的道路构成树状结构:首都1与几个大城市相连,这几个大城市又通过道路与一些稍小的城市相连……严格地说,这N座城市构成一棵有根树(1为树根),城市i的管理区域为以i为根的子树。
道路都是双向的。经过每条道路需要收费,从城市A到城市B的花费为A到B的简单路径上所有道路的费用之和(不妨称之为城市对(A, B)之间的花费)。显然,一棵树上任意两点之间的简单路径(即不能重复经过某个点的路径)是唯一的。
由于物价飞涨,经过一番缜密的思考,乐乐决定重新规划自己国家的道路收费。具体来说,他要进行Q个操作,每个操作是如下两种类型:
INC u v w
——表示u到v的路径上,所有的道路的收费增加w;
ASK p
——表示询问城市p的管理区域中,所有的“城市对”的花费之和。比如,城市p的管理区域(即以p为根子树)中有城市 c 1 , c 2 , c 3 , c s (这里面包括p),询问所有的城市对 ( c 1 , c 2 ) , ( c 1 , c 3 ) , , ( c 2 , c 3 ) , , ( c s 1 , c s ) 的花费之和。
乐乐把这个问题交给你,快帮帮他吧!
输入描述:
第一行输入两个正整数N,Q,分别表示城市的数目和操作的数目。
接下来有N – 1行,第i行是两个正整数 p i , c i ,表示城市 p i 是城市 i 的父亲结点,且连接 p i i 的道路的初始收费为 c i 1 c i 1000
接下来有Q行,每行是如下两种类型之一:
INC u v w (u, v, w都是整数,且 1 u , v N , 0 w 1000 ,注意u, v可能相等)
ASK p (p是正整数,且 p N )
意义如题目所述。
输出描述:
对每个ASK类型的操作,输出所求的答案。请你输出答案对2019取模后的结果。
示例1
输入
5 5
1 1
2 5
1 2
2 1
INC 2 4 2
INC 3 4 1
ASK 2
INC 2 5 3
ASK 1
输出
14
84
备注:
1 Q , N 50000 ,树的深度不超过10000,其他输入数据的范围在输入格式中已给出。

题解:考虑当我们询问点 x 的时候, x 的子树内每条边的贡献。对于在 x 的子树内的点 p ,记 p 到父亲的边的权值为 a p
则根据乘法原理,在这次询问中这条边被经过的次数为 s i z e p × ( s i z e x s i z e p ) 即产生的贡献为
a p × s i z e p × ( s i z e x s i z e p ) = a p × s i z e p × s i z e x a p × s i z e p × s i z e p
注意到 s i z e x 是我们询问的时候已知的,因此每个询问就相当于在问这 2 个东西.

a p × s i z e p × s i z e x
a p × s i z e p × s i z e p

然后这个可以用树链剖分做了。
对于更新操作,利用树链剖分在线段树上更新,那么增加 w
a n s = ( a p + w ) × s i z e p × s i z e x ( a p + w ) × s i z e p × s i z e p
那么在线段树里用懒惰标记 w

对于查询操作,求出原先的答案加上懒惰标记对答案的影响即可。

#include<bits/stdc++.h>
using namespace std;
const int MAX=5e4+10;
const int MOD=2019;
typedef long long ll;
int val[MAX];
vector<int>e[MAX];
int fa[MAX],son[MAX],d[MAX],siz[MAX],L[MAX],R[MAX],top[MAX],num[MAX],all=0;
void dfs1(int k,int dep)
{
    son[k]=0;
    d[k]=dep;
    siz[k]=1;
    for(int i=0;i<e[k].size();i++)
    {
        dfs1(e[k][i],dep+1);
        siz[k]+=siz[e[k][i]];
        if(siz[e[k][i]]>siz[son[k]])son[k]=e[k][i];
    }
}
void dfs2(int k,int tp)
{
    L[k]=++all;
    num[all]=k;
    top[k]=tp;
    if(son[k])dfs2(son[k],tp);
    for(int i=0;i<e[k].size();i++)
    {
        if(e[k][i]==son[k])continue;
        dfs2(e[k][i],e[k][i]);
    }
    R[k]=all;
}
struct lenka
{
    int l,r;
    int tag;       //懒惰标记
    int siz,siz2;  //记录区间内所有节点的siz总和以及siz^2的总和
    int sum1,sum2; //记录答案中的前一部分sum1和后一部分sum2
}A[MAX<<2];
void build(int k,int l,int r)
{
    A[k].l=l,A[k].r=r;
    if(l==r)
    {
        A[k].siz2=1ll*siz[num[r]]*siz[num[r]]%MOD;
        A[k].siz=siz[num[r]]%MOD;
        A[k].sum1=1ll*val[num[r]]*siz[num[r]]%MOD;
        A[k].sum2=1ll*val[num[r]]*siz[num[r]]*siz[num[r]]%MOD;
        return;
    }
    build(2*k,l,(l+r)/2);
    build(2*k+1,(l+r)/2+1,r);
    A[k].sum1=(A[2*k].sum1+A[2*k+1].sum1)%MOD;
    A[k].sum2=(A[2*k].sum2+A[2*k+1].sum2)%MOD;
    A[k].siz=(A[2*k].siz+A[2*k+1].siz)%MOD;
    A[k].siz2=(A[2*k].siz2+A[2*k+1].siz2)%MOD;
}
void add(int k,int x,int y,int z)
{
    if(x==A[k].l&&y==A[k].r)
    {
        if(x!=y)A[k].tag+=z;
        (A[k].sum1+=1ll*A[k].siz*z%MOD)%=MOD; //更新前一部分的和
        (A[k].sum2+=1ll*A[k].siz2*z%MOD)%=MOD;//更新后一部分的和
        return;
    }
    if(A[k].tag)
    {
        add(2*k,A[2*k].l,A[2*k].r,A[k].tag);
        add(2*k+1,A[2*k+1].l,A[2*k+1].r,A[k].tag);
        A[k].tag=0;
    }
    if(y<=A[2*k].r)add(2*k,x,y,z);
    else if(x>=A[2*k+1].l)add(2*k+1,x,y,z);
    else
    {
        add(2*k,x,A[2*k].r,z);
        add(2*k+1,A[2*k+1].l,y,z);
    }
    A[k].sum1=(A[2*k].sum1+A[2*k+1].sum1)%MOD;
    A[k].sum2=(A[2*k].sum2+A[2*k+1].sum2)%MOD;
}
int ask(int k,int x,int y,int root)
{
    if(x==A[k].l&&y==A[k].r)return (1ll*A[k].sum1*siz[root]%MOD-A[k].sum2+MOD)%MOD;
    if(A[k].tag)
    {
        add(2*k,A[2*k].l,A[2*k].r,A[k].tag);
        add(2*k+1,A[2*k+1].l,A[2*k+1].r,A[k].tag);
        A[k].tag=0;
    }
    if(y<=A[2*k].r)return ask(2*k,x,y,root);
    if(x>=A[2*k+1].l)return ask(2*k+1,x,y,root);
    return (ask(2*k,x,A[2*k].r,root)+ask(2*k+1,A[2*k+1].l,y,root))%MOD;
}
void QWQ(int x,int y,int z)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(d[top[x]]<d[top[y]])swap(x,y);
        add(1,L[top[x]],L[x],z);
        x=fa[top[x]];
    }
    if(x==y)return;
    if(d[x]>d[y])swap(x,y);
    add(1,L[son[x]],L[y],z);
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=2;i<=n;i++)
    {
        scanf("%d%d",&fa[i],&val[i]);
        e[fa[i]].push_back(i);
    }
    dfs1(1,1);
    dfs2(1,1);
    build(1,1,all);
    while(m--)
    {
        char op[5];
        scanf("%s",op);
        if(strcmp(op,"INC")==0)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            QWQ(x,y,z);
        }
        else
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",(ask(1,L[x],R[x],x)%MOD+MOD)%MOD);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Mitsuha_/article/details/81710411