D - Circular Coloring(dp j个数分成i段)

原题: http://acm.hdu.edu.cn/showproblem.php?pid=6279

题意:

有n个0,m个1,组成一个环(循环同构算不同的),连续的0或1串的贡献为其长度,一个串的值为所有贡献的乘积。问所有情况的值之和。

解析:

假设已经得出将j个数分成i段的答案(不是环),记为 d p [ i ] [ j ] dp[i][j]

显然在环中0和1的段数相同,所以 a n s = i = 1 m i n ( n , m ) ( d p [ i ] [ n ] + d p [ i ] [ m ] ) ans=\sum_{i=1}^{min(n,m)}(dp[i][n]+dp[i][m])

但是现在在环上,如果循环同构只算一次,那么考虑i段的情况会多记录i-1次(123,231,321),再考虑两种和到一起后可以旋转n+m次(循环同构算不同的),所以变为: a n s = i = 1 m i n ( n , m ) ( d p [ i ] [ n ] + d p [ i ] [ m ] ) ( n + m ) i ans=\sum_{i=1}^{min(n,m)}\dfrac{(dp[i][n]+dp[i][m])*(n+m)}{i}

再考虑dp的求解。

显然 d p [ i ] [ j ] = d p [ i 1 ] [ j 1 ] + 2 d p [ i 1 ] [ j 2 ] + 3 d p [ i 1 ] [ j 3 ] . . . dp[i][j]=dp[i-1][j-1]+2dp[i-1][j-2]+3dp[i-1][j-3]...
d p [ i ] [ j 1 ] = d p [ i 1 ] [ j 2 ] + 2 d p [ i 1 ] [ j 3 ] . . . dp[i][j-1]=dp[i-1][j-2]+2dp[i-1][j-3]...
所以 d p [ i ] [ j ] = d p [ i ] [ j 1 ] + ( d p [ i 1 ] [ j 1 ] + d p [ i 1 ] [ j 2 ] + d p [ i 1 ] [ j 3 ] . . . ) dp[i][j]=dp[i][j-1]+(dp[i-1][j-1]+dp[i-1][j-2]+dp[i-1][j-3]...)
用一个前缀和就可以实现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 );
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/89959144
今日推荐