期望题集--[BJOI2018]治疗之雨

题目链接:https://www.luogu.org/problemnew/show/P4457

发现不能直接记忆化搜索,因为当前可能状态可能转移到血量+1的状态,但可以发现如果血量为上限值时就没有这种情况了,因此先写出每一个状态的转移式,为一串其他状态*系数+常数,然后从第n位下来把每一个状态转移式中>=当前状态血量的状态消去,再dfs即可。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=1510;

ll n,p,m,k,f[N],xs[N][N],cs[N],C[N],jc[N],ijc[N],X,Y,g[N];

void Ad(ll &x,ll y)
{x+=y;if(x>=mod)x-=mod;}
void Dw(ll &x,ll y)
{x=(x>=y)?x-y:x-y+mod;}
ll qpow(ll x,ll y)
{
    ll res=1;
    while(y)
    {
        if(y&1)res=res*x%mod;
        x=x*x%mod,y>>=1;
    }
    return res;
}

void f__k(int x)
{
    ll tp;
    if(xs[x][x+1])
    {
        tp=xs[x][x+1];
        xs[x][x+1]=0;
        for(int i=x;i>=0;i--)
            Ad(xs[x][i],tp*xs[x+1][i]%mod);
        Ad(cs[x],tp*cs[x+1]%mod);
    }
    if(xs[x][x])
    {
        tp=1;
        Dw(tp,xs[x][x]);
        tp=qpow(tp,mod-2);
        xs[x][x]=0;
        for(int i=0;i<x;i++)
            xs[x][i]=xs[x][i]*tp%mod;
        cs[x]=cs[x]*tp%mod;
    }
    
}
void get_xs(ll x)
{
    for(int i=0;i<=min(k,x);i++)
        xs[x][x+1-i]=X*Y%mod*C[i]%mod*g[i]%mod;
    for(int i=0;i<=min(k,x);i++)
        Ad(xs[x][x-i],m*Y%mod*X%mod*C[i]%mod*g[i]%mod);
    cs[x]=1;
}
ll dfs(int nw)
{
    if(f[nw]!=-1)return f[nw];
    f[nw]=cs[nw];
    for(int i=nw-1;i>=0;i--)
        Ad(f[nw],xs[nw][i]*dfs(i)%mod);
    return f[nw];
}
bool check()
{
    if(k==0)return 1;
    if(m==0)
    {
        if(k==1)
        {
            if(n>1)return 1;
        }
    }
    return 0;
}
int main()
{
    int T;ll tp;
    jc[0]=1;
    for(int i=1;i<N;i++)
        jc[i]=jc[i-1]*i%mod;
    ijc[N-1]=qpow(jc[N-1],mod-2);
    for(int i=N-2;i>=0;i--)
        ijc[i]=ijc[i+1]*(i+1)%mod;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld%lld",&n,&p,&m,&k);
        if(check()){puts("-1");continue;}
        memset(xs,0,sizeof xs),memset(cs,0,sizeof cs);
        for(int i=0;i<=p;i++)f[i]=-1;f[0]=0,tp=1;
        for(int i=0;i<=n&&i<=k;i++)
        {
            C[i]=tp*ijc[i]%mod;
            tp=tp*(k-i)%mod;
        }
        X=qpow(m+1,k),X=qpow(X,mod-2),Y=qpow(m+1,mod-2);
        for(int i=0;i<=n&&i<=k;i++)
            g[i]=qpow(m,k-i);
        for(int j=0;j<=min(k,n);j++)
            xs[n][n-j]=X*C[j]%mod*g[j]%mod;
        cs[n]=1,f__k(n);
        for(int i=n-1;i>=1;i--)
            get_xs(i),f__k(i);
        printf("%lld\n",dfs(p));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值