zoukankan      html  css  js  c++  java
  • 题解 Crash 的文明世界

    题目传送门

    题目大意

    给出一个(n)个点的树,和常数(k),对于(forall iin[1,n]),求出:

    [sum_{j=1}^{n} ext{dist}(i,j)^k ]

    (nle 5 imes 10^4,kle 150)

    思路

    真的很妙,一开始完全没有思路,看了( exttt{y2823774827y})的题解之后瞬间懂了。

    我们考虑对于(i)如何计算答案,我们发现这个指数非常不好看,于是我们可以使用第二类斯特林数展开,就跟组合数问题差不多的,变为:

    [sum_{j=1}^{n}sum_{d=0}^{ ext{dist}(i,j)}inom{ ext{dist}(i,j)}{d}egin{Bmatrix}k\dend{Bmatrix}d! ]

    交换求和顺序可以得到:

    [=sum_{d=0}^{min(n,k)}egin{Bmatrix}k\dend{Bmatrix}d!sum_{j=1}^{n}inom{ ext{dist}(i,j)}{d} ]

    于是,我们的问题就是如何快速求出后面那个(sum)。我们想到这个东西可以拆成:

    [inom{ ext{dist}(i,j)}{d}=inom{ ext{dist}(i,j)-1}{d}+inom{ ext{dist}(i,j)-1}{d-1} ]

    于是,我们用换根(dp)解决这个问题了。具体见代码。

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define MAXN 50005
    #define mod 10007
    #define MAXM 155
    
    int qkpow (int a,int b){
    	int res = 1;for (;b;b >>= 1,a = 1ll * a * a % mod) if (b & 1) res = 1ll * res * a % mod;
    	return res;
    }
    
    int mul (int a,int b){return 1ll * a * b % mod;}
    int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
    int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
    
    struct edge{
    	int v,nxt; 
    }e[MAXN << 1];
    
    int top = 1,head[MAXN];
    
    void Add_Edge (int u,int v){
    	e[++ top] = edge {v,head[u]},head[u] = top;
    	e[++ top] = edge {u,head[v]},head[v] = top;
    } 
    
    int n,k,S[MAXM][MAXM],fac[MAXM],dp1[MAXN][MAXM],dp2[MAXN][MAXM],tmp[MAXM];
    //dp1[u][k]表示的是sum_{j在i的子树内(包括i)} inom{dist(i,j)}{k} 
    //dp2[u][k]表示的是sum_{j=1}^{n} inom{dist(i,j)}{k}
    
    void dfs1 (int u,int fa){
    	dp1[u][0] = 1;
    	for (Int i = head[u];i;i = e[i].nxt){
    		int v = e[i].v;
    		if (v == fa) continue;
    		dfs1 (v,u);
    		for (Int j = 1;j <= k;++ j) dp1[u][j] = add (dp1[u][j],add (dp1[v][j],dp1[v][j - 1]));
    		dp1[u][0] = add (dp1[u][0],dp1[v][0]);
    	}
    }
    
    void dfs2 (int u,int fa){//换根dp 
    	for (Int i = 0;i <= k;++ i) dp2[u][i] = dp1[u][i];
    	if (fa){
    		for (Int i = 1;i <= k;++ i) tmp[i] = dec (dp2[fa][i],add (dp1[u][i],dp1[u][i - 1]));
    		tmp[0] = dec (dp2[fa][0],dp1[u][0]);
    		for (Int i = 1;i <= k;++ i) dp2[u][i] = add (dp2[u][i],add (tmp[i],tmp[i - 1]));
    		dp2[u][0] = add (dp2[u][0],tmp[0]);
    	} 
    	for (Int i = head[u];i;i = e[i].nxt){
    		int v = e[i].v;
    		if (v == fa) continue;
    		dfs2 (v,u);
    	}
    }
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    signed main(){
    	read (n,k),S[0][0] = fac[0] = 1;
    	for (Int i = 1;i <= k;fac[i] = mul (i,fac[i - 1]),++ i)
    		for (Int j = 1;j <= i;++ j)
    			S[i][j] = add (S[i - 1][j - 1],mul (j,S[i - 1][j]));
    	for (Int i = 2,u,v;i <= n;++ i) read (u,v),Add_Edge (u,v);
    	dfs1 (1,0),dfs2 (1,0);
    	for (Int i = 1;i <= n;++ i){
    		int sum = 0;
    		for (Int j = 0;j <= k;++ j) sum = add (sum,mul (fac[j],mul (S[k][j],dp2[i][j])));
    		write (sum),putchar ('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    重复的listen port引发的问题
    Win10开始运行不保存历史记录原因和解决方法
    意识到const版本函数的重要性
    人物访谈1
    人物访谈2
    读《浪潮之巅》有感
    测试作业
    读《活出生命的意义》有感
    价值观作业
    关于C语言的问卷调查
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13383677.html
Copyright © 2011-2022 走看看