• 快速讨伐


    快速讨伐 ( 一道DP与组合数学的思维好题 )

    传送门

    前置小技巧 (线性求阶乘的逆元)

    根据逆元的定义 : (a*x equiv 1 (mod p))
    所以相当于 $ x== $ (1over a) (在膜意义下)
    那么 (1over !(n+1)) 也就相当于 !(n+1) 的逆元,
    所以 !n 的逆元就可以看做 (1over !n) 也就是(1over !(n+1))$ *(n+1)$

    ps: 此技巧多用于求组合数时(如下题)

    题解

    显然不管什么情况操作次数一定是固定的,为 (2*n+) (sum_{i=1}^n) (a_i)

    我们先不考虑角色等级的情况

    即默认初始等级为 n (即已经操作了n次,且全部用来提升人物等级)
    那么对于第i次提升装备等级就可以多打败 Ai 个的敌人,
    也就是在剩余的操作中,可以任选 Ai 个位置来进行此操作
    则会有 (inom{ n-i+sum_{k=i}^n Ak}{a_i}) 种选择
    (sum_{k=i}^n Ak) 是A从i开始的后缀和)
    然后显然递推下去就好

    再考虑普通情况

    那么只多了一个操作限制,就是人物等级必须不小于装备等级
    不妨DP一下,设 f[i][j] 表示人物已经 i 级,装备 j 级,则显然 可以由f[i][j]推出 f[i+1][j] 与 f[i][j+1]

    而对于 f[i+1][j] 显然和 f[i][j] 的剩余操作选择方案是一样的

    那么

    我们再来考虑一下 f[i][j+1] 的情况
    通过之前的特殊情况,若多升了一次装备等级,则选择方案会等于
    (f[i][j]* inom{ n+n-i-j-1+sum_{k=j+1}^n Ak}{a_{j+1}})

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define ll long long
    #define in inline
    #define get getchar()
    in int read()
    {
    	int t=0; char ch=get;
    	while( ch<'0' || ch>'9') ch=get;
    	while( ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    	return t;
    }
    const int _=5005;
    const int mod=998244353;
    ll sum[_],n,m,a[_],c1[_],c2[_],f[_][_];
    in ll fastpow(ll a,int b)
    {
    	ll res=1;
    	while(b)
    	{
    		if(b%2==1)
    			res=(res*a)%mod;
    		a=a*a%mod,b>>=1;
    	}
    	return res;
    }
    in ll C(ll x,ll y)
    {
    	return c1[x]*c2[y]%mod*c2[x-y]%mod;
    }
    int main()
    {
    	n=read(),m=0;
    	for(re int i=1;i<=n;i++)
    		a[i]=read();
    	for(re int i=n;i;i--) sum[i]=(sum[i+1]+a[i])%mod; //sum存的是后缀和	
    	int tot=2*n+sum[1]; c1[0]=1;
    	for(re int i=1;i<=tot;i++) c1[i]=(c1[i-1]*i)%mod; // c1中存储阶乘
    	c2[tot]=fastpow(c1[tot],mod-2);
    	for(re int i=tot-1;i>=0;i--)c2[i]=c2[i+1]*(i+1)%mod; // c2中存储阶乘的逆元
    	f[m][0]=1;
    	for(re int i=m;i<=n;i++)
    		for(re int j=0;j<=i;j++)
    			if(f[i][j])
    			{
    				if(i<n) f[i+1][j]=(f[i+1][j]+f[i][j])%mod;
    				if(j<i) 
                         f[i][j+1]=(f[i][j+1]+(f[i][j]*C(n+n-i-j+sum[j+1]-1,a[j+1]))%mod)%mod;
    			}
    	cout<<f[n][n]<<endl;
    }
    
  • 相关阅读:
    课堂作业1
    懒人创造了方法
    四则运算
    动手动脑与原码反码补码
    java测试感受
    暑假进度报告四
    暑假进度报告三
    暑假进度报告二
    暑假进度报告一
    《大道至简》读后感
  • 原文地址:https://www.cnblogs.com/yzhx/p/11530278.html
Copyright © 2020-2023  润新知