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);
    	}
    }
    
  • 相关阅读:
    MySQL5.6 GTID、多线程复制
    WPS for Linux(ubuntu)字体配置(字体缺失解决办法)
    linux下用phpize给PHP动态添加扩展
    Zabbix汉化方法
    [FTP] Pure-FTPd SSL/TLS 配置方法
    PHP 缓存扩展opcache
    sftp搭建
    nginx https使用
    iptables基本规则
    kvm虚拟机安装
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9431203.html
Copyright © 2011-2022 走看看