CF107D Crime Management
我们发现乘积小于等于 $\text{123}$ ,设 $mul_i=\prod\limits_{j=1}^{c}m_j[t_j = i]$ ,就是相当于这一种限制的积,然后我们发现在每一种的个数对 $mul_i$ 取膜的情况下,状态数最多只有 $123$ 种,就可以考虑把状态压缩为一维。
发现根本不需要搜出所有状态再给编号,考虑一个二维矩阵中的坐标我们是如何把它转成一个数的,这里就相当于多维的坐标,编号就可以变为:
$((r_1 \times mul_2+r_2)\times mul_3+r_3)...$ (这里 $r_i$ 为对 $mul_i$ 取膜以后的数)
所以可以进行 $\text{DP}$
然而 $n \le 10^{18}$ ,发现对于从选 $i$ 个到选 $i+1$ 个,怎么转移与 $i$ 无关,可以在一开始处理出来,所以每一次转移其实本质上都一样,就可以直接套矩阵快速幂了
注意最后答案不是 $f_0$ ,因为有些种类虽然膜 $mul_i$ 不为 $\text{0}$ ,但是可能膜某一个 $m_j$ 为 $0$ ,需要枚举每种状态是否可行,然后可行就加上即可
$code$ :
#include<cstdio> #include<cctype> #include<vector> using namespace std; #define maxn 33 #define maxs 222 #define maxc 1111 #define mod 12345 inline long long read(){ long long r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } inline char get_c(){ char c; while(!isalpha(c=getchar())); return c; } int N=1; long long n; int cnt,mul[maxn]; long long ans,f[maxs],a[maxs][maxs],c[maxs][maxs]; vector<int> m[maxn]; inline void mulself(){ for(int i=0;i<N;i++) for(int j=0;j<N;j++) for(int k=0;k<N;k++) (c[i][j]+=a[i][k]*a[k][j]%mod)%=mod; for(int i=0;i<N;i++) for(int j=0;j<N;j++){ a[i][j]=c[i][j]; c[i][j]=0; } } inline void multi(){ for(int i=0;i<N;i++) for(int j=0;j<N;j++) (c[0][i]+=f[j]*a[i][j]%mod)%=mod; for(int i=0;i<N;i++){ f[i]=c[0][i]; c[0][i]=0; } } int main(){ n=read(),cnt=read(); for(int i=1;i<=cnt;i++){ int t=get_c()-'A'+1; int val=read(); m[t].push_back(val); if(!mul[t])mul[t]=1; mul[t]*=val; }//没有限制的种类是不能选的,所以mul开始要全置0 for(int i=1;i<=26;i++) if(mul[i])N*=mul[i]; N++; for(int i=0;i<N;i++){//枚举对于每种状态 int num=i;//选一种以后能转移到哪一个 long long mult=1; for(int j=26;j>=1;j--){ if(!mul[j])continue; int tot=num%mul[j]; int s=i-tot*mult; tot++; tot%=mul[j]; s+=tot*mult; num/=mul[j]; mult*=mul[j]; a[s][i]++;//存在该转移 } } f[0]=1; for(;n;n>>=1){ if(n&1)multi(); mulself(); } for(int i=0;i<N;i++){//找合法状态 int num=i; bool ok=1; for(int j=26;j>=1;j--){ if(!mul[j])continue; int tot=num%mul[j]; bool b=0; for(int k=0;k<(int)m[j].size();k++) b|=((tot%m[j][k])==0);//只要满足一种即可 ok&=b; num/=mul[j]; } if(ok)(ans+=f[i])%=mod;//合法就加上,记得取膜 } printf("%lld\n",ans); return 0; }