zoukankan      html  css  js  c++  java
  • [题解] [CF995F] Cowmpany Cowmpensation

    题面

    题解

    (f[i][j]) 为以 (i) 为根的子树在 (i) 选的权值为 (j) 时的方案数
    (g[i][j] = sum_{k = 1}^{j} f[i][k])

    [f[u][j] = prod g[v][j], v in {Son_u} ]

    然而这是一个 (O(nd)) 的算法
    考虑到这个树中最多只会出现 (n) 种不同的权值
    那么我们改一下 DP 式, 设 (f[i][j]) 为以 (i) 为根的子树在 (i) 选的权值离散化后是第 (j) 个的方案数
    还是像上面一样的转移
    下文中的权值即为离散化之后的大小
    那么我们发现, (f[1][i]) 包含了这棵树中最大权值为 (i) 的所有方案数
    但是可能这棵树中有一些点选的值是相同的, 也就是这棵树中权值的总数要小于 (i)
    考虑容斥这个东西, 设 (h[i]) 为树中恰好有 (i) 种权值的方案数
    不难得到

    [h[i] = f[1][i] - sum_{j=1}^{i-1}{{i-1}choose{j-1}}h[j]\ ans = sum_{i=1}^{min(n, d)}{dchoose i}h[i] ]

    这样就可以 (O(n^2)) 地算出答案了

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 3005;
    const int mod = 1e9 + 7; 
    using namespace std;
    
    int n, m, c[N][N], inv[N], fac[N], f[N][N], g[N], head[N], cnte, ans; 
    struct edge { int to, nxt; } e[N << 1]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    inline void adde(int u, int v) { e[++cnte] = (edge) { v, head[u] }, head[u] = cnte; }
    
    void dfs(int u, int fa)
    {
    	for(int v, i = head[u]; i; i = e[i].nxt)
    	{
    		v = e[i].to; if(v == fa) continue;
    		dfs(v, u); 
    		for(int j = 1; j <= n; j++)
    			f[u][j] = 1ll * f[u][j] * f[v][j] % mod; 
    	}
    	if(fa)
    		for(int i = 1; i <= n; i++) 
    			   f[u][i] = (f[u][i] + f[u][i - 1]) % mod; 
    }
    
    int fpow(int x, int y)
    {
    	int res = 1; 
    	for( ; y; y >>= 1, x = 1ll * x * x % mod)
    		if(y & 1) res = 1ll * res * x % mod; 
    	return res; 
    }
    
    int main()
    {
    	n = read <int> (), m = read <int> (); 
    	for(int u, i = 2; i <= n; i++)
    		u = read <int> (), adde(i, u), adde(u, i); 
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= n; j++)
    			f[i][j] = 1; 
    	dfs(1, 0);
    	for(int i = (fac[0] = 1); i <= n; i++)
    		fac[i] = 1ll * fac[i - 1] * i % mod; 
    	inv[n] = fpow(fac[n], mod - 2); 
    	for(int i = n - 1; i >= 0; i--)
    		inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; 
    	for(int i = 0; i <= n; i++)
    		for(int j = 0; j <= i; j++)
    			c[i][j] = !j ? 1 : (c[i - 1][j] + c[i - 1][j - 1]) % mod; 
    	for(int i = 1; i <= n; i++)
    	{
    		g[i] = f[1][i];
    		for(int j = 1; j < i; j++)
    			g[i] = (1ll * g[i] - 1ll * c[i - 1][j - 1] * g[j] % mod + mod) % mod; 
    	}
    	for(int tmp = 1, i = 1; i <= min(n, m); i++)
    	{
    		tmp = inv[i]; 
    		for(int j = m; j > m - i; j--)
    			tmp = 1ll * tmp * j % mod; 
    		ans = (ans + 1ll * tmp * g[i] % mod) % mod; 
    	}
    	printf("%d
    ", ans); 
    	return 0; 
    }
    
  • 相关阅读:
    【Java例题】3.1 7、11、13的倍数
    【Java例题】2.7找零钱
    【Java例题】2.6 三角形的面积
    【Java例题】2.5 温度转换
    【Java例题】2.4求函数
    【Java例题】2.2 分数类
    【Java例题】2.3 计算银行存款本息
    博客园里面关于abpZero的好的教程
    IOC学习1
    1.开始第一个MVC项目
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12796547.html
Copyright © 2011-2022 走看看