• 「清华集训 2017」小 Y 和恐怖的奴隶主


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

    传送门

    Loj

    题解

    讲道理,这是一道简单题.

    首先可以考虑暴力(dp),很显然可以设(f_{i,a,b,c})表示(i)次攻击后,有(a)个一滴血的随从,(b)个两滴血的,(c)个三滴血的.

    然后这个东西状态最多(166),考虑直接矩阵快速幂.

    注意到这是一个行向量( imes)矩阵,所以复杂度是(O(k^2logn)),其他的矩阵预处理即可.

    代码

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define REP(a,b,c) for(int a=b;a<=c;a++)
    #define re register
    #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    typedef pair<int,int> pii;
    #define mp make_pair
    inline int gi()
    {
    	int f=1,sum=0;char ch=getchar();
    	while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    	return f*sum;
    }
    const int N=210,Mod=998244353;
    int a[N],A[N],m,k,tot,lim[4],Hash[19][19][19],mat[62][N][N],tmp[N];
    ll n;
    void dfs(int a,int b,int c)
    {
    	if(a>lim[1]||b>lim[2]||c>lim[3])return;
    	if(Hash[a][b][c]||a+b+c>k)return;
    	Hash[a][b][c]=++tot;
    	dfs(a+1,b,c);dfs(a,b+1,c);dfs(a,b,c+1);
    }
    int qpow(int a,int b){int ret=1;while(b){if(b&1)ret=1ll*ret*a%Mod;b>>=1;a=1ll*a*a%Mod;}return ret;}
    void calc(int &a,int &b,int &c){(m==1)?a++:(m==2)?b++:c++;}
    void init()
    {
    	for(int a=0;a<=lim[1];a++)
    		for(int b=0;b+a<=k&&b<=lim[2];b++)
    			for(int c=0;a+b+c<=k&&c<=lim[3];c++)
    			{
    				int s=a+b+c+1,p=Hash[a][b][c];
    				mat[0][p][tot+1]=(mat[0][p][tot+1]+qpow(s,Mod-2))%Mod;
    				mat[0][p][p]=(mat[0][p][p]+qpow(s,Mod-2))%Mod;
    				if(a)
    				{
    					int ta=a-1,tb=b,tc=c;
    					mat[0][p][Hash[ta][tb][tc]]=(mat[0][p][Hash[ta][tb][tc]]+1ll*qpow(s,Mod-2)*a%Mod)%Mod;
    				}
    				if(b)
    				{
    					int ta=a+1,tb=b-1,tc=c;if(s<=k)calc(ta,tb,tc);
    					mat[0][p][Hash[ta][tb][tc]]=(mat[0][p][Hash[ta][tb][tc]]+1ll*qpow(s,Mod-2)*b%Mod)%Mod;
    				}
    				if(c)
    				{
    					int ta=a,tb=b+1,tc=c-1;if(s<=k)calc(ta,tb,tc);
    					mat[0][p][Hash[ta][tb][tc]]=(mat[0][p][Hash[ta][tb][tc]]+1ll*qpow(s,Mod-2)*c%Mod)%Mod;
    					
    				}
    			}
    	mat[0][tot+1][tot+1]=(mat[0][tot+1][tot+1]+1)%Mod;
    	A[Hash[m==1][m==2][m==3]]=1;
    	for(int p=1;p<=60;p++)
    		for(int i=1;i<=tot+1;i++)
    			for(int k=1;k<=tot+1;k++)
    				for(int j=1;j<=tot+1;j++)
    					mat[p][i][j]=(mat[p][i][j]+1ll*mat[p-1][i][k]*mat[p-1][k][j]%Mod)%Mod;
    }
    int main()
    {
    	int T=gi();m=gi();k=gi();
    	for(int i=1;i<=m;i++)lim[i]=k;
    	dfs(0,0,0);init();
    	while(T--)
    	{
    		scanf("%lld",&n);int b=0;
    		for(int i=1;i<=tot+1;i++)a[i]=A[i];
    		while(n)
    		{
    			if(n&1ll)
    			{
    				for(int i=1;i<=tot+1;i++)tmp[i]=a[i],a[i]=0;
    				for(int k=1;k<=tot+1;k++)
    					for(int j=1;j<=tot+1;j++)
    						a[j]=(a[j]+1ll*tmp[k]*mat[b][k][j]%Mod)%Mod;
    			}
    			b++;n>>=1ll;
    		}
    		printf("%d
    ",a[tot+1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    关于在UNIcode环境下得TCHAR转string类型以及string转TCHAR
    c++重要知识点
    c语言五子棋
    修改单词首字母大小写
    MFC界面分割以及挂载
    c语言操作文件函数大全
    字符串的分割
    简单售货机代码
    Oracle数据库的查询
    oracle数据库四大语言
  • 原文地址:https://www.cnblogs.com/fexuile/p/12854640.html
Copyright © 2020-2023  润新知