1. 程式人生 > >Comet OJ - contest #3 C DP

Comet OJ - contest #3 C DP

範圍 代碼 我們 子序列 long long 繼續 所有 update light

題意:給你一個長度為n序列,和一個數m,問這個序列有多少個子序列,滿足這個子序列的所有子序列的和是m的倍數?答案對1e9 + 7取模,n, m範圍到5e3;

思路:容易發現,如果一個子序列的長度是n,子序列的所有的元素的和是sum的話,它的所有的子序列的和是sum * 2 ^ (n - 1),那麽我們發現,一個序列的所有子序列的和與子序列的和以及子序列的長度有關,我們容易想O(n^2 * m)的DP。設dp[i][j][k]為前i個數,長度為j的子序列中子序列的和是k的元素的個數。每掃到一個新的元素,有兩種決策:1:不加這個數dp[i + 1][j][k] += dp[i][j][k];2:加這個數dp[i + 1][j +1][k + a[i + 1] += dp[i][j][k]。每次轉移O(n * m),總復雜度O(n ^ 2 * m).

我們現在考慮優化一下dp。我們發現一個序列的子序和與2 ^ (n - 1)與sum有關,若要子序和是m的倍數,分兩種情況:1:2不是m的因子,那麽容易發現2 ^ (n - 1)不會影響子序和是否是m的倍數。2:2是m的因子,但是m最大範圍是5e3,所以最大有2 ^ 12這個因子,而所以當n大於12的時候又變成了情況1.所以,實際上dp的第二維的大小只有十幾,復雜度降為了O(n * logn * m),但是這樣的復雜度仍然不夠優秀,我們考慮繼續優化。我們可以發現,dp[i][j][k]中,隨著j的增加,k那一維的模數也在不斷減少,模數是m + m / 2 + m / 4 ...這個復雜度是O(m)的,所以我們逐步優化之後,復雜度降低到了O(n * m)。

代碼:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 5010;
const LL mod = 1000000007;
int a[maxn], dp[2][15][maxn], p[20];
void update(int &x, int y) {
	x = ((long long)x + y) % mod;
}
int main() {
	int n, m;
	int ans = 0;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	int cnt = 0;
	int tmp = m;
	while(tmp % 2 == 0) {
		tmp /= 2;
		cnt++;
	}
	dp[0][0][0] = 1;
	for (int i = 0; i < n; i++) {
		memset(dp[(i + 1) & 1], 0, sizeof(dp[(i + 1) & 1]));
		for (int j = 0; j <= cnt + 1; j++) {
			memset(dp[(i + 1) & 1][j], 0, sizeof(int) * (m / (1 << max(0, j - 1))));
		}
		for (int j = 0; j <= cnt; j++) {
			int mm = m / (1 << max(0, j - 1)), mmm = m / (1 << max(0, j));
			for (int k = 0; k < mm; k++) {
				if(!dp[i & 1][j][k]) continue;
				update(dp[(i + 1) & 1][j][k], dp[i & 1][j][k]);
				update(dp[(i + 1) & 1][j + 1][(k + a[i + 1]) % mmm], dp[i & 1][j][k]);
			}
		}
		for (int j = 0; j < tmp; j++) {
			if(!dp[i & 1][cnt + 1][j]) continue;
			update(dp[(i + 1) & 1][cnt + 1][j], dp[i & 1][cnt + 1][j]);
			update(dp[(i + 1) & 1][cnt + 1][(j + a[i + 1]) % tmp], dp[i & 1][cnt + 1][j]);
		}
	}
	for (int i = 1; i <= cnt + 1; i++) {
		update(ans, dp[n & 1][i][0]);
	}
	printf("%d\n", ans);
	return 0;
} 

  

Comet OJ - contest #3 C DP