hdu 5291 dp+优化 ****
多校实在高能
题意:有n中糖果,每种糖果有ai个。分给A,B两个人。两人的糖果要一样多,可以都是0,1......m个。同一种糖果没有区别。
问有几种分法。
定义dp[i]表示两人之间相差i个糖果的情况数。对每种糖果进行处理 *dp[i]表示新计算得到的dp值
当当前有ai个i种糖果时。处理*dp[j]
*dp[j] = dp[j]*(ai/2+1) + dp[j-1]*((ai-1)/2+1) + dp[j+1]*((ai-1)/2+1) ............+dp[j-ai]*((ai-ai)/2+1) + dp[j+ai]*((ai-ai)/2+1)
表示dp[j-k]*((ai-k)/2+1)表示原来A,B相差j-k个糖果,但是通过分第i种糖果,先给A分了k个糖果,让后剩下的糖果再平等 地分给A,B。那么分完之后就相差j个糖果了。由于提前给A,k个糖果,那么剩下ai-k个糖果,平分剩下糖果的情况有(ai-k)/2+1种。+1是两 个人人都分0个。
假设ai = 2 j = 0, 那么dp[0] =
dp: dp[-2] dp[-1] dp[0] dp[1] dp[2]
系数:1 1 2 1 1
算出*dp[0]之后,算*dp[1] = *dp[0] + dp[1] + dp[3] - dp[0] - dp[-2]
可以发现此时只要把[j+1,j+1+ai]的奇数位置的dp值加起来 - [j-ai,j]偶数位置的dp值 + *dp[0] = *dp[1]
转移变成O(1)的了。
假设ai = 3 j = 0, 那么dp[0] =
dp: dp[-3] dp[-2] dp[-1] dp[0] dp[1] dp[2] dp[3]
系数:1 1 2 2 2 1 1
同ai = 2相反。找规律即可。
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 #define ll long long 7 int dp[90000],sum[2][90000]; 8 int mod = 1000000007; 9 int modx = -mod; 10 int num[300]; 11 int main(){ 12 int t,n; 13 scanf("%d",&t); 14 while(t--){ 15 scanf("%d",&n); 16 int total = 0; 17 for(int i = 1; i <= n; i++){ 18 scanf("%d",&num[i]); 19 total += num[i]; 20 } 21 if(total & 1) total++; 22 memset(dp,0,sizeof(dp)); 23 memset(sum,0,sizeof(sum)); 24 dp[total] = 1; 25 int tt = total*2; 26 for(int i = 1; i <= n; i++) 27 { 28 sum[0][0] = dp[0]; 29 sum[1][0] = 0; 30 for(int j = 1;j <= tt; j++) 31 { 32 sum[0][j] = sum[0][j-1]; 33 sum[1][j] = sum[1][j-1]; 34 sum[j&1][j] += dp[j]; 35 sum[j&1][j] %= modx; 36 } 37 ll ans = 0; 38 for(int j = 0;j <= num[i]; j++){ 39 ans += (ll)dp[j]*((num[i]-j)/2+1); 40 ans %= mod; 41 } 42 int p = (num[i]&1)^1; 43 int res = ans; 44 for(int j = 0;j <= tt; j++) 45 { 46 dp[j] = res; 47 int u = j-num[i]-1; 48 u = max(u,0); 49 res += (sum[p][j+1+num[i]] - sum[p][j])%mod; 50 res %= mod; 51 p ^= 1; 52 res -= (sum[p][j] - sum[p][u])%mod; 53 res %= mod; 54 } 55 } 56 int res = dp[total]; 57 res %= mod; 58 res = (res+mod)%mod; 59 cout<<res<<endl; 60 } 61 return 0; 62 }