• 8.27 神异之旅


    题意

    对于所有大小为(k)正整数可重集合,要求(sum_{i=1}^ka_i =n)

    定义满足上述要求的正整数可重集合的权值为

    [sum_{i=1}^k a_i^m ]

    求所有满足条件的集合的权值之和,模(10^9+7)

    其实luogu上有原题,(P4977),那道题数据范围更大,还需要滚动数组优化


    解法

    由于权值是相加进行计算的,我们可以把每一部分的权值分开计算

    比如说对于一个数(a_i)

    它在所有的集合中出现的次数之和为(cnt_i)

    那么这个数(a_i)做出的贡献就是(a_i^m imes cnt_i)

    现在的问题就是如何求出这个(cnt_i)

    再次转化一下这个问题,我们可以(O(N))枚举这个数在集合中出现的次数(x),乘上出现次数为(x)的集合个数,累加起来即为最终的答案

    现在的问题转化为了求出现次数为(x)的集合个数

    (f[n][k])为权值和为(n),共有(k)个元素的集合个数,那么出现次数至少为(x)的集合个数即为(f[n-x imes a_i][k-x])

    但是上面这个是出现至少为(x)次的,并不是恰好为(x)次的

    这个问题怎么解决呢

    可以想到用容斥,但是有一个更简单的方法

    我们最后要求的是(cnt_i),原来我们的想法是出现次数乘上出现次数是该数的集合个数:

    现在我们只需要累加出现次数是该数的集合个数了

    这样相当于每次加一层,加完就统计出所有答案了

    问题再一次转化(保证是最后一次了),我们现在的任务是求出(f[n][k])

    考虑进行(DP),具体问题可参考(P1025)数的划分

    这个(DP)的思路很巧妙

    我们把一个状态分成两类:一类其集合中有(1)这个元素,一类中没有,两类分别转移相加

    具体来说就是

    (f[n][k]=f[n][k]+f[n-1][k-1])

    这一类是集合内有(1)的个数

    (f[n][k]=f[n][k]+f[n-k][k])

    这一类是集合内无(1)的个数,可以看成是(f[n-k][k])中所有的元素都加一个(1),这样就可以保证集合内无(1)


    代码

    #include <cstdio>
    #include <cctype>
    
    using namespace std;
    
    const int mod = 1e9 + 7;
    
    int n, k, m;
    
    int f[5010][5010];
    
    inline int qpow(int x, int y) {
    	int res = 1;
    	while (y) {
    		if (y & 1)	res = 1LL * res * x % mod;
    		x = 1LL * x * x % mod, y >>= 1;	
    	}
    	return res;
    }
    
    int main() {
    	
    	freopen("set.in", "r", stdin);
    	freopen("set.out", "w", stdout);
    	
    	scanf("%d%d%d", &n, &k, &m);
    	
    	for (int i = 1; i <= n; ++i)	f[i][1] = 1;
    	for (int i = 2; i <= n; ++i) {
    		for (int j = 2; j <= k && j <= i; ++j) {
    			f[i][j] = (f[i][j] + f[i - 1][j - 1]) % mod;
    			f[i][j] = (f[i][j] + f[i - j][j]) % mod;
    		}
    	}
    	
    	int ans = 0;
    	for (int i = 1; i <= n; ++i) {
    		int sum = 0;
    		for (int j = 1; i * j <= n && j <= k; ++j)
    			sum = (sum + f[n - i * j][k - j]) % mod;
    		ans = (ans + 1LL * sum * qpow(i, m) % mod) % mod;
    	}
    	
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    How does “void *” differ in C and C++?
    Can we use function on left side of an expression in C and C++?
    apache配置局域网访问
    apache以天为单位生成日志
    尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
    IIS 处理程序“PageHandlerFactory-Integrated”
    IIS无法识别的属性targetFramework
    php开启短标签支持
    Notepad++配色方案
    vim常用操作整理
  • 原文地址:https://www.cnblogs.com/VeniVidiVici/p/11426249.html
Copyright © 2020-2023  润新知