题目链接http://acm.hdu.edu.cn/showproblem.php?pid=6279
题意:
给出n个白点,m个黑点,连成一个环。一个环的价值是所有相邻且颜色相同的长度的乘积。问所有环价值的和
题解:
d
p
[
i
]
[
j
]
[
2
]
dp[i][j][2]
dp[i][j][2]表示第一维的点有
i
i
i个,第二维有
j
j
j个,与开头是否相同的,链的权值和。默认第一维是开头。
转移方程应该是:
- d p [ i ] [ j ] [ 0 ] = ∑ k d p [ k ] [ j ] [ 1 ] ∗ ( i − k ) dp[i][j][0]=\sum_{k}dp[k][j][1]*(i-k) dp[i][j][0]=∑kdp[k][j][1]∗(i−k)
- d p [ i ] [ j ] [ 1 ] = ∑ k d p [ i ] [ k ] [ 0 ] ∗ ( j − k ) dp[i][j][1]=\sum_{k}dp[i][k][0]*(j-k) dp[i][j][1]=∑kdp[i][k][0]∗(j−k)
这个可以做两次前缀和来进行优化。
直接写空间会炸掉,可以对前缀和数组压一维。
d
p
[
i
]
[
j
]
[
1
]
dp[i][j][1]
dp[i][j][1]是没用的,直接变成中间变量,也省了一维
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+7;
const int mod=1e9+7;
int dp[N][N],tmp;
int s0[N],ss0[N];
int s1[N],ss1[N];
int main(){
for(int i=1;i<N;i++){
dp[i][0]=s0[i]=ss0[i]=i;
}
for(int i=1;i<N;i++){
for(int j=1;j<N;j++){
tmp=ss0[i];
dp[i][j]=ss1[j];
s0[i]=(s0[i]+dp[i][j])%mod;
ss0[i]=(ss0[i]+s0[i])%mod;
s1[j]=(s1[j]+tmp)%mod;
ss1[j]=(ss1[j]+s1[j])%mod;
}
}
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+1ll*i*i%mod*dp[m][n-i]%mod)%mod;
}
for(int i=1;i<=m;i++){
ans=(ans+1ll*i*i%mod*dp[n][m-i]%mod)%mod;
}
printf("%lld\n",ans);
}
}