原题: http://acm.hdu.edu.cn/showproblem.php?pid=6279
题意:
有n个0,m个1,组成一个环(循环同构算不同的),连续的0或1串的贡献为其长度,一个串的值为所有贡献的乘积。问所有情况的值之和。
解析:
假设已经得出将j个数分成i段的答案(不是环),记为 。
显然在环中0和1的段数相同,所以 。
但是现在在环上,如果循环同构只算一次,那么考虑i段的情况会多记录i-1次(123,231,321),再考虑两种和到一起后可以旋转n+m次(循环同构算不同的),所以变为: 。
再考虑dp的求解。
显然
而
所以
用一个前缀和就可以实现O(1)维护。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod=1e9+7;
LL dp[5009][5009];
LL sum[2][5009];
LL inv[5009];
int main(){
inv[1]=1;
for(int i=2;i<=5000;i++)inv[i]=inv[mod%i]*(mod-mod/i)%mod;
dp[1][1]=1;
int ff=0;
sum[ff][1]=1;
for(int i=2;i<=5000;i++)
dp[1][i]=i,sum[ff][i]=(sum[ff][i-1]+dp[1][i])%mod;
for(int i=2;i<=5000;i++){
dp[i][i]=1;
sum[!ff][i]=1;
for(int j=i+1;j<=5000;j++){
dp[i][j]=(dp[i][j-1]+sum[ff][j-1])%mod;
sum[!ff][j]=(sum[!ff][j-1]+dp[i][j])%mod;
}
ff=!ff;
}
int n,m;
while(cin>>n>>m){
LL ans=0;
for(int i=1;i<=min(n,m);i++){
ans+=(dp[i][n]*dp[i][m])%mod*inv[i]%mod;
}
ans%=mod;
printf("%lld\n",(ans*(n+m)%mod+mod)%mod );
}
}