[bzoj2159] Crash的文明世界 [斯特林数+树形dp]
2018-08-06 16:53
417 查看
题面
bzoj权限题离线题面
思路
首先,有一个自然数幂的拆分公式:$x^n=\sum_{i=1}^n \begin{Bmatrix} n&n \\ i&i \end{Bmatrix}=\frac{x!}{(x-i)!}$其中$\begin{Bmatrix} n \\ i \end{Bmatrix}$是第二类斯特林数
那么本题中我们显然也可以利用这个公式,把原式变成如下的形式:
$S(i)=\sum_{j=1}^ndis(i,j)^m=\sum_{j=1}^n\sum_{k=1}^m \begin{Bmatrix} m \\ k \end{Bmatrix} \binom{dis(i,j)}{k}k!$
更换一下枚举方式
$S(i)=\sum_{k=1}^m \begin{Bmatrix} m \\ k \end{Bmatrix} k! \sum_{j=1}^n\binom{dis(i,j)}{k}$
然后由于每条边的长度都是1,所以就可以用组合数的递推公式$\binom{n}{m}=\binom{n-1}{m-1}+\binom{n-1}{m}$来树形$DP$算后面那个$\sum$了~
具体而言,我们定义两个状态$down[u][k]$和$up[u][k]$,分别表示$u$的子树中的点和除了$u$子树之外的所有点,在枚举到$k$时,对$u$的答案的贡献
那么显然可以先一遍$dfs$算出$down$,方程为$down[u][k]=\sum_{v=son(u)}down[v][k]+down[v][k-1]$,原理就是上面那个公式
然后第二遍$dfs$算$up$,这个东西要复杂一些
首先由公式显然可得$up[u][k]=up[fa][k]+up[fa][k-1]$,但是这还没有算$fa$这个点,和$fa$的其它子树的贡献
我们可以用$down[fa][k]-down[x][k]-down[x][k-1]$和$down[fa][k-1]-down[x][k-1]-down[x][k-2]$来表示这个贡献,也就是把$fa$的$down$里面减掉$x$做出的贡献
所以综合来说,$up[u][k]=up[fa][k]+up[fa][k-1]+down[fa][k]-down[x][k]-down[x][k-1]+down[fa][k-1]-down[x][k-1]-down[x][k-2]$
那么最后只要对于每个点$u$枚举$k$,把所有的$up[u][k]+down[u][k]$同前面的东西乘起来累加,就得到了点$u$的答案
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cassert> #include<cmath> #define ll long long #define MOD 10007 using namespace std; inline int read(){ int re=0,flag=1;char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') flag=-1; ch=getchar(); } while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar(); return re*flag; } int n,m,first[50010],cnte,s[200][200],fac[200]; struct edge{ int to,next; }a[100010]; inline void add(int u,int v){ a[++cnte]=(edge){v,first[u]};first[u]=cnte; a[++cnte]=(edge){u,first[v]};first[v]=cnte; } void init(){//预处理阶乘和斯特林数 int i,j;fac[0]=fac[1]=1; for(i=2;i<=m;i++) fac[i]=fac[i-1]*i%MOD; s[0][0]=1; for(i=1;i<=m;i++){ for(j=0;j<=i;j++){ s[i][j]=s[i-1][j-1]+s[i-1][j]*j;s[i][j]%=MOD; } } } int down[50010][210],up[50010][210],ans[50010]; void dfs1(int u,int f){ int i,v,k; down[u][0]=1; for(i=first[u];~i;i=a[i].next){ v=a[i].to;if(v==f) continue; dfs1(v,u); down[u][0]+=down[v][0];//down[u][0]相当于siz[u],可以自己证证 for(k=1;k<=m;k++) down[u][k]+=down[v][k]+down[v][k-1],down[u][k]%=MOD; } } void dfs2(int u,int f){ int i,v,k; if(!f) goto skip;//没有fa,哪来的up? up[u][0]=n-down[u][0]; for(k=1;k<=m;k++){ up[u][k]=up[f][k]+up[f][k-1]+down[f][k]+down[f][k-1]-down[u][k]-down[u][k-1]-down[u][k-1]; if(k>1) up[u][k]-=down[u][k-2]; up[u][k]=(up[u][k]%MOD+MOD)%MOD; } skip: for(i=first[u];~i;i=a[i].next){ v=a[i].to;if(v==f) continue; dfs2(v,u); } } int main(){ memset(first,-1,sizeof(first)); n=read();m=read();int i,A,B,Q,lim,now,tmp,k; lim=read();now=read();A=read();B=read();Q=read(); for(i=1;i<n;i++){ now=(now*A+B)%Q; tmp=(i<lim)?i:lim; add(i-now%tmp,i+1); } init();dfs1(1,0);dfs2(1,0); for(i=1;i<=n;i++){ for(k=1;k<=m;k++) ans[i]+=s[m][k]*fac[k]%MOD*(up[i][k]+down[i][k])%MOD; printf("%d\n",ans[i]%MOD); } }
相关文章推荐
- [BZOJ2159]Crash的文明世界(斯特林数+树形DP)
- 【BZOJ2159】Crash的文明世界(树形DP,斯特林数的性质)
- bzoj 2159: Crash 的文明世界 树形dp
- [第二类斯特林数 树形DP] HDU 4625 JZPTREE && BZOJ 2159 Crash 的文明世界
- bzoj 2159: Crash 的文明世界 树形dp+第二类斯大林数+排列组合
- BZOJ2159 Crash 的文明世界 【第二类斯特林数 + 树形dp】
- [斯特林数][DP]BZOJ 2159: Crash 的文明世界
- BZOJ2159 Crash的文明世界(树形dp+斯特林数)
- BZOJ2159:Crash 的文明世界 (第二类stirling数+组合数学+树形DP)
- BZOJ2159 Crash 的文明世界 题解
- bzoj 2159: Crash 的文明世界
- 【bzoj 2159】Crash 的文明世界
- bzoj 2159 - Crash 的 文明世界
- [BZOJ2159]Crash的文明世界 树型DP+第二类Striling数
- BZOJ2159 : Crash 的文明世界
- [bzoj 2159]Crash的文明世界
- 2159: Crash 的文明世界
- BZOJ 4199: [Noi2015]品酒大会/UOJ #131. 【NOI2015】品酒大会 后缀自动机 树形dp / 后缀数组 单调栈
- bzoj 3611 【heoi2014】大工程 虚树+树形DP
- BZOJ_1999_[Noip2007]Core树网的核_单调队列+树形DP