链接: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为根子树)中有城市
(这里面包括p),询问所有的城市对
的花费之和。
乐乐把这个问题交给你,快帮帮他吧!
输入描述:
第一行输入两个正整数N,Q,分别表示城市的数目和操作的数目。
接下来有N – 1行,第i行是两个正整数
,表示城市
是城市
的父亲结点,且连接
和
的道路的初始收费为
。
接下来有Q行,每行是如下两种类型之一:
INC u v w (u, v, w都是整数,且
,注意u, v可能相等)
ASK p (p是正整数,且
)
意义如题目所述。
输出描述:
对每个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
备注:
,树的深度不超过10000,其他输入数据的范围在输入格式中已给出。
题解:考虑当我们询问点
的时候,
的子树内每条边的贡献。对于在
的子树内的点
,记
到父亲的边的权值为
。
则根据乘法原理,在这次询问中这条边被经过的次数为
即产生的贡献为
注意到
是我们询问的时候已知的,因此每个询问就相当于在问这 2 个东西.
然后这个可以用树链剖分做了。
对于更新操作,利用树链剖分在线段树上更新,那么增加
后
那么在线段树里用懒惰标记
。
对于查询操作,求出原先的答案加上懒惰标记对答案的影响即可。
#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;
}