题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:
- 操作 1 :把某个节点 x 的点权增加 a 。
- 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
- 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入输出格式
输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
输出格式:
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
输入输出样例
说明
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
题解:树链泼粪,上代码
#include<cstdio> #include<string> #include<cstring> #include<algorithm> using namespace std; long long f[2001][2001]; struct line { int s,t; long long x; int next; }a[4001]; int head[2001]; long long tmp[2001]; int edge; inline void add(int s,int t,long long x) { a[edge].next=head[s]; head[s]=edge; a[edge].s=s; a[edge].t=t; a[edge].x=x; } bool v[2001]; int size[2001]; int n,kk; inline void trdp(int d) { v[d]=true; size[d]=1; int i; int j,k; for(i=head[d];i!=0;i=a[i].next) { int t=a[i].t; if(!v[t]) { trdp(t); for(j=0;j<=kk;j++) tmp[j]=f[d][j]; for(j=0;j<=min(size[d],kk);j++) { for(k=0;k<=min(size[t],kk);k++) { if(j+k>kk) break; //int m=j+k; tmp[j+k]=max(tmp[j+k],f[d][j]+f[t][k]+a[i].x*(k*(kk-k)+(size[t]-k)*(n-kk-(size[t]-k)))); } } size[d]+=size[t]; for(j=0;j<=size[d];j++) f[d][j]=max(f[d][j],tmp[j]); } } } int main() { scanf("%d%d",&n,&kk); int i; int s,t; long long x; for(i=1;i<=n-1;i++) { scanf("%d%d%lld",&s,&t,&x); edge++; add(s,t,x); edge++; add(t,s,x); } trdp(1); printf("%lld ",f[1][kk]); return 0; }