不错的树形$ DP$的题
可为什么我自带大常数啊$ cry$
链接:
题意:给定一棵$ n$个节点的树,边权为$ 1$,对于每个点$ x$求$ \sum\limits_{i=1}^n dist(x,i)^k$
数据范围:$ n<=50000,k<=150$
$ Solution$
直接推式子
上下$ DP$先考虑子树内的贡献
有$ in(x)^k=\sum\limits (in(son[x])+1)^k$
这是因为自己子树内的每个点到自己的距离都$ ++$
再考虑子树外的贡献
有$ out(x)^k=(out(fa[x])+1)^k+(in(fa[x])+1)^k-(in(x)+2)^k$
这是因为父亲节点子树外的节点和父亲子树中除自己外其他子树内的节点到自己的距离相比原先都$ ++$
而自己这棵子树内不增反减所以再$ -=2$
直接二项式展开是$ nk^2$的
不过这类式子可以转化成斯特林数
我们只要从求$ in(x)^k/out(x)^k$转化成求$ C_{in(x)/out(x)}^k$即可
这样$ C_{in(x)}^k=\sum\limits C_{in(son[x])+1}^k=\sum\limits C_{in(son[x])}^k+C_{in(son[x])}^{k-1}$
求$ out$的时候同理,唯一的区别是$ C_{in(x)+2}^k$需要展开两层
代码还是比较清真的
$ my \ code$
#include#include #include #include #include #include #include #define p 10007#define M 100010#define rt register int#define ll long longusing namespace std;inline ll read(){ ll x = 0; char zf = 1; char ch = getchar(); while (ch != '-' && !isdigit(ch)) ch = getchar(); if (ch == '-') zf = -1, ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;}void write(ll y){ if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}void writeln(const ll y){write(y);putchar('\n');}int i,j,k,m,n,x,y,z,cnt;int val[50010][155],out[50010][155],fa[50010]; int F[M],L[M],N[M],a[M];void add(int x,int y){ a[++k]=y; if(!F[x])F[x]=k; else N[L[x]]=k; L[x]=k;}void dfs(int x,int pre){ fa[x]=pre;val[x][0]=1; for(rt i=F[x];i;i=N[i])if(a[i]!=pre){ dfs(a[i],x); (val[x][0]+=val[a[i]][0])%=p; } out[x][0]=n-val[x][0];}void get(int x){ for(rt i=F[x];i;i=N[i])if(a[i]!=fa[x]){ get(a[i]); for(rt j=1;j<=m;j++) (val[x][j]+=val[a[i]][j]+val[a[i]][j-1])%=p; } }int S[155][155],inv[155];void up(int x){ for(rt j=1;j<=m;j++) if(x==1)out[x][j]=0; else out[x][j]=(out[fa[x]][j]+out[fa[x]][j-1]+val[fa[x]][j]+val[fa[x]][j-1]-(val[x][j-1]*2+val[x][j]+val[x][j-2]))%p; for(rt i=F[x];i;i=N[i])if(a[i]!=fa[x])up(a[i]);}int main(){ n=read();m=read(); for(rt i=1;i