HDU-6377 度度熊看球赛(正向dp与快速幂)

Problem Description

世界杯正如火如荼地开展!度度熊来到了一家酒吧。

有 NNN 对情侣相约一起看世界杯,荧幕前正好有 2×N2 \times N2×N 个横排的位置。

所有人都会随机坐在某个位置上。

当然,如果某一对情侣正好挨着坐,他们就会有说不完的话,影响世界杯的观看。

一般地,对于一个就座方案,如果正好有 KKK 对情侣正好是挨着坐的,就会产生 DKD^KD​K​​ 的喧闹值。

度度熊想知道随机就座方案的期望喧闹值。

为了避免输出实数,设答案为 ansansans,请输出 ans×(2N)!ans \times (2N)!ans×(2N)! modmodmod PPP 的值。其中 P=998244353P=998244353P=998244353

Input

有多组数据(不超过 100010001000 组),读到EOF结束。

对于每一组数据,读入两个数 NNN 和 DDD 。

扫描二维码关注公众号,回复: 2743249 查看本文章

1≤N,D≤10001 \leq N,D \leq 10001≤N,D≤1000

Output

对于每一组数据,输出一个数表示答案。

Sample Input

Copy

1 10
2 3

Sample Output

Copy

20
104

 额,看了题解写的。。。。。。。

因为这个dp仅仅是枚举,并没有求最优状态,所以正向是完全没问题的,那这样是不是说最优只能逆向思维呢,额,姑且看成是这样吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1010
#define p 998244353
long long dp[MAXN][MAXN];      //dp[i][j]表示i对人有j对情侣坐在一起
long long pow_mod(long long a,long long x)
{
	long long ans=1;               //p是取模的数
    long long base=a%p;
	while(x>0)
	{
       if((x&1)==1)
       ans=(ans*base)%p;     //if里的条件也可以写成x&1
	   base=base*base%p;
	   x=x>>1;
	}
	return ans;
}
long long N,D;
void Init()
{
    memset(dp,0,sizeof(dp));
    int i,j;
    dp[1][0]=0;
    dp[1][1]=1;             //先把一组情侣的两个人看做是相同的(最后方案数要乘上2^i)
    for(i=1;i<=1000;i++)       //i<MAXN;就会很耗时间。
    for(j=0;j<=i;j++)
    {
        //每次考虑把第 i+1 对情侣放进去
        //也就是后一状态。
        //这对情侣合在一起放。
        // 1.放在某一对挨着的情侣中间(拆散他们):
        dp[i+1][j]+=dp[i][j]*j%p;              //因为dp加起来还是很大的
        dp[i+1][j]%=p;
        //2.放在情侣之间的空隙里:
        dp[i+1][j+1]+=dp[i][j]*(2*i-j+1)%p;   //2*i+1个缝隙减去j个情侣之间的缝隙。
        dp[i+1][j+1]%=p;
        //这对情侣分开放
        //他们各自都拆散了一对情侣:
        if(j>=2)
        {
             dp[i+1][j-2]+=dp[i][j]*j*(j-1)/2%p; //C(j,2)
             dp[i+1][j-2]%=p;
        }
        //只有一个人拆散了一对情侣:
        if(j>=1)
        {
            dp[i+1][j-1]+=dp[i][j]*(2*i-j+1)*j%p;  //(2*i+1-j)个其他空隙,j个那个空隙,你懂的,额
            dp[i+1][j-1]%=p;
        }
        //没有情侣被拆散
        dp[i+1][j]+=dp[i][j]*(2*i-j+1)*(2*i-j)/2%p; //C(....,2)
        dp[i+1][j]%=p;
    }
}
int main()
{
    //顺推的话,自然就是找之后的状态,这里是对数加一,也就是说是加第一个for的参数
    //这应该是顺推的DP,因为逆推太难了,本来逆推的话,就是找前一状态,前一状态好找的话,就省很多时间,
    //其实不妨假设用逆推,dp[i][j]表示i对人有j对人坐在一起
    //排列组合用DP正推。
    Init();
    while(scanf("%lld%lld",&N,&D)!=EOF)
    {
        long long k;
        long long  ans=0;
        for(k=0;k<=N;k++)
        {
            long long temp=pow_mod(D,k);
            temp*=pow_mod(2,N);             //这两个一乘就超int了
            temp%=p;
            ans+=dp[N][k]*temp%p;     //反正加取模,再取模就是连环加模
            ans%=p;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/81607752
今日推荐