loj#2325. 「清华集训 2017」小 Y 和恐怖的奴隶主

传送门

解题思路:

p[i][j][k][l] p [ i ] [ j ] [ k ] [ l ] 表示 i i 回合奴隶主 123 血剩余情况为 jkl j 、 k 、 l 的概率,那么对答案的贡献就是 p[i][j][k][l]j+k+l+1 p [ i ] [ j ] [ k ] [ l ] j + k + l + 1

先预处理出状态及转移,共有165种状态,加上求和的一行就166,再用矩阵快速幂。
注意多组询问,考虑到一个行向量乘以一个方阵的时间时 O(n2) O ( n 2 ) 的,因此可以倍增预处理出转移矩阵的 2i 2 i 次幂,然后把每个矩阵依次乘到行向量上即可。

时间复杂度为 O(1663logn+T1662logn) O ( 166 3 l o g n + T 166 2 l o g n ) ,注意细节

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll getint()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=170,mod=998244353;
inline void add(ll &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int T,m,k,tot,id[10][10][10],vis[10][10][10];
struct matrix 
{
    ll a[N][N];
    matrix(){memset(a,0,sizeof(a));}
    inline friend matrix operator * (const matrix &A,const matrix &B)
    {
        matrix res;
        for(int i=0;i<=tot;i++)
            for(int k=0;k<=tot;k++)if(A.a[i][k])
                for(int j=0;j<=tot;j++)
                    res.a[i][j]=(res.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
        return res;
    }
}A,B,P[65];
ll Pow(ll x,int y)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}
void dfs(int c1,int c2,int c3)
{
    if(c1+c2+c3>k||id[c1][c2][c3])return;
    id[c1][c2][c3]=tot++;
    if(m>=1)dfs(c1+1,c2,c3);
    if(m>=2)dfs(c1,c2+1,c3);
    if(m>=3)dfs(c1,c2,c3+1);
}
void dfs1(int c1,int c2,int c3)
{
    if(c1+c2+c3>k||vis[c1][c2][c3])return;
    vis[c1][c2][c3]=1;ll inv=Pow(1+c1+c2+c3,mod-2);
    ll p0=inv,p1=c1*inv%mod,p2=c2*inv%mod,p3=c3*inv%mod;
    add(B.a[id[c1][c2][c3]][tot],p0);
    add(B.a[id[c1][c2][c3]][id[c1][c2][c3]],p0);
    if(c1)add(B.a[id[c1][c2][c3]][id[c1-1][c2][c3]],p1);
    if(c2)
    {
        if(c1+c2+c3<k)
        {
            if(m==2)add(B.a[id[c1][c2][c3]][id[c1+1][c2][c3]],p2);
            if(m==3)add(B.a[id[c1][c2][c3]][id[c1+1][c2-1][c3+1]],p2);
        }
        else add(B.a[id[c1][c2][c3]][id[c1+1][c2-1][c3]],p2);
    }
    if(c3)
    {
        if(c1+c2+c3<k)add(B.a[id[c1][c2][c3]][id[c1][c2+1][c3]],p3);
        else add(B.a[id[c1][c2][c3]][id[c1][c2+1][c3-1]],p3);
    }
    if(m>=1)dfs1(c1+1,c2,c3);
    if(m>=2)dfs1(c1,c2+1,c3);
    if(m>=3)dfs1(c1,c2,c3+1);
}
void solve(ll n)
{
    memset(A.a,0,sizeof(A.a));
    if(m==1)A.a[0][id[1][0][0]]=1;
    if(m==2)A.a[0][id[0][1][0]]=1;
    if(m==3)A.a[0][id[0][0][1]]=1;
    for(int i=0;i<=60;i++)
        if(n>>i&1)A=A*P[i];
    cout<<A.a[0][tot]<<'\n';
}
int main()
{
    //freopen("lx.in","r",stdin);
    T=getint(),m=getint(),k=getint();
    dfs(0,0,0),dfs1(0,0,0);B.a[tot][tot]=1;
    P[0]=B;for(int i=1;i<=60;i++)P[i]=P[i-1]*P[i-1];
    while(T--)solve(getint());
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值