zoukankan      html  css  js  c++  java
  • [bzoj2159] Crash的文明世界 [斯特林数+树形dp]

    题面

    bzoj权限题

    离线题面

    思路

    首先,有一个自然数幂的拆分公式:$xn=sum_{i=1}n egin{Bmatrix} n&n \ i&i end{Bmatrix}=frac{x!}{(x-i)!}$

    其中$egin{Bmatrix} n \ i end{Bmatrix}$是第二类斯特林数

    那么本题中我们显然也可以利用这个公式,把原式变成如下的形式:

    $S(i)=sum_{j=1}ndis(i,j)m=sum_{j=1}nsum_{k=1}m egin{Bmatrix} m \ k end{Bmatrix} inom{dis(i,j)}{k}k!$

    更换一下枚举方式

    $S(i)=sum_{k=1}^m egin{Bmatrix} m \ k end{Bmatrix} k! sum_{j=1}^ninom{dis(i,j)}{k}$

    然后由于每条边的长度都是1,所以就可以用组合数的递推公式$inom{n}{m}=inom{n-1}{m-1}+inom{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
    ",ans[i]%MOD);
    	}
    }
    
  • 相关阅读:
    401. Binary Watch
    46. Permutations
    61. Rotate List
    142. Linked List Cycle II
    86. Partition List
    234. Palindrome Linked List
    19. Remove Nth Node From End of List
    141. Linked List Cycle
    524. Longest Word in Dictionary through Deleting
    android ListView详解
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9431203.html
Copyright © 2011-2022 走看看