【原题地址】: Codeforces107D
【题目大意】: 有一种长度为 (n) 的字符串,给定 (m) 种限制,即字符 (c) 出现的次数为 (cnt),若一个字符有多种限制,则满足任意一个即可,求这种字符串有多少个,所有的 $cnt $ 相乘小于等于 (123)。
【题解】: 我们可以考虑DP的做法
我们设 maxn[c]
为第 (c) 种字符的所有限制 (cnt) 的乘积
dp[i][a][b][c][d].....[z]
,表示枚举到字符串的第 (i) 位,每个字符为,出现次数 (\%) maxn[c]
的状态下字符串的总数。
那么最后求答案直接统计合法的方案就行了。
但是这么做空间,时间都是不允许的,所以我们要使用矩阵快速幂来进行优化。
我们可以考虑使用 (map) 来对每种状态重新编号,将状态存入矩阵中进行转移。
矩阵第 (i) 行第 (j) 列如果有值 1,则说明可以转移,最后矩阵的 (n-1) 次幂
就是状态转移了 (n) 次,直接统计第 1 行的合法方案。
我统计答案,预处理矩阵用的DFS
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<vector>
using namespace std;
const int mod=12345;
long long n;
int m,ans=0;
int maxn[30];
vector<int>ok[30];
struct Matrix
{
int a[125][125];
Matrix()
{
memset(a,0,sizeof(a));
};
Matrix operator * (const Matrix &x) const
{
Matrix ans=Matrix();
for(int i=1;i<=123;i++)
for(int j=1;j<=123;j++)
{
for(int k=1;k<=123;k++)ans.a[i][j]+=a[i][k]*x.a[k][j]%mod,ans.a[i][j]%=mod;
}
return ans;
}
Matrix operator ^ (const long long &x) const
{
Matrix ans=(*this),temp=(*this);
long long now=x;
while(now)
{
if(now&1)ans=ans*temp;
temp=temp*temp;
now>>=1;
}
return ans;
}
}mat;
struct dat
{
int val[30];
bool operator < (const dat& a)const
{
for(int i=1;i<=26;i++)
{
if(val[i]==a.val[i])continue;
return val[i]<a.val[i];
}
return false;
}
}chuzhi;
map<dat,int>mp;
int pcnt=0;
void dfs(int pos,dat now)
{
if(pos==27)
{
mp[now]=++pcnt;
return ;
}
if(maxn[pos]==0)dfs(pos+1,now);
for(int i=0;i<maxn[pos];i++)
{
now.val[pos]=i;
dfs(pos+1,now);
}
}
void calc(int pos,dat now)
{
if(pos==27)
{
ans+=mat.a[1][mp[now]];
ans%=mod;
return ;
}
if(maxn[pos]==0)calc(pos+1,now);
for(int i=0;i<maxn[pos];i++)
{
bool flag=false;
for(int j=0;j<ok[pos].size();j++)
if(i%ok[pos][j]==0){
flag=true;
break;
}
if(flag)
{
now.val[pos]=i;
calc(pos+1,now);
}
}
}
char A;
int x,c;
int main()
{
scanf("%lld%d",&n,&m);
if(n==0)//特判
{
printf("1
");
return 0;
}
if(m==0)//特判
{
printf("0
");
return 0;
}
for(int i=1;i<=m;i++)
{
scanf(" %c %d",&A,&x);
c=A-'A'+1;
if(maxn[c]==0)maxn[c]=x;
else maxn[c]*=x;
ok[c].push_back(x);
}
dfs(1,chuzhi);
for(map<dat,int>::iterator i=mp.begin();i!=mp.end();i++)
{
int s=i->second;
dat now=i->first;
for(int j=1;j<=26;j++)
{
if(maxn[j]==0)continue;
dat temp=now;
temp.val[j]=(temp.val[j]+1)%maxn[j];
mat.a[s][mp[temp]]++;
}
}
mat=mat^((long long)n-1);
calc(1,chuzhi);
printf("%d
",ans);
return 0;
}