[CF107D]Crime Management
Posted Tan_tan_tann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF107D]Crime Management相关的知识,希望对你有一定的参考价值。
Crime Management
题解
简单dp
首先,我们看到 n ⩽ 1 0 18 n\\leqslant 10^{18} n⩽1018的限制,应该很容易知道该题肯定是通过矩阵之类的方法来计算答案,毕竟什么直接组合的明显是不行的。
原题相当于让我们求出使得每个字符都满足其中之一的限制,每个限制相当于让某个字符的出现次数模上
c
n
t
cnt
cnt为
0
0
0。我们可以考虑将每个字符的出现次数模后的值计入状态,构建状压
d
p
dp
dp。
对于模数为
c
n
t
cnt
cnt的,它有
c
n
t
cnt
cnt种可能模后得到的值,所以它对状态的贡献为
c
n
t
cnt
cnt。
很显然,对于不同的字符的限制的状态是独立的。
而同一字符的不合法状态明显不可能被走到,合法状态由于带模,就像中国剩余定理一样,相互之间也没有影响。
对于字符
c
h
ch
ch,我让
c
n
t
c
h
m
o
d
5
=
3
cnt_{ch}mod\\,5=3
cntchmod5=3与
c
n
t
c
h
m
o
d
7
=
2
cnt_{ch}mod\\,7=2
cntchmod7=2两者之间其实是没有冲突的,可以同时存在。
我们可以将每个限制的每个余数都加入状态,于是我们最后的状态有
∏
i
=
1
m
c
n
t
i
\\prod_{i=1}^{m}cnt_{i}
∏i=1mcnti种。
由于题目已经告诉我们
∏
i
=
1
m
c
n
t
i
⩽
123
\\prod_{i=1}^{m}cnt_{i}\\leqslant 123
∏i=1mcnti⩽123,这完全是可以构建
d
p
dp
dp的。
既然已经构建出了
d
p
dp
dp状态,也看到
n
⩽
1
0
18
n\\leqslant 10^{18}
n⩽1018,我们很容易往矩阵上想。
考虑如何构建转移矩阵,也就是原
d
p
dp
dp状态是怎么转移的。
很明显,我们每次的操作相当于在原字符串后面添加一个字符,我们看看添加这个字符时会对哪些限制的余数造成影响,也就可以知道它会转移到哪些状态了。
而我们知道余数后又将其表达到状态上其实就是个简单的哈希过程,我们只需要记录下来每个限制余数的权重即可。
通过矩阵快速幂求出答案矩阵后,我们就只需要检验一下哪些状态是合法的,将这些状态对应得
d
p
dp
dp值加入答案即可。
其实这种通过矩阵转移来求字符串构建方法数的手段是很常见的,CF上许多题目都所涉及。
我们记
ω
=
∏
c
n
t
⩽
123
\\omega=\\prod cnt\\leqslant 123
ω=∏cnt⩽123
时间复杂度
O
(
ω
3
log
n
+
ω
m
∣
C
∣
)
O\\left(\\omega^3\\log\\,n+\\omega m|C|\\right)
O(ω3logn+ωm∣C∣)。
其实后面一段如果将有
c
n
t
cnt
cnt值为一的限制的字符的限制保留
c
n
t
cnt
cnt为
1
1
1的限制的话其实可以被压缩到
O
(
∣
C
∣
(
∣
C
∣
+
log
ω
)
ω
)
O\\left(|C|(|C|+\\log\\omega)\\omega\\right)
O(∣C∣(∣C∣+logω)ω),但实际上时间复杂度主要集中在前边,影响也不大。
源码
看错了好几遍题
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=12345;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int m,nd,num,tot,b[30];LL n,ans;bool vt[30],ok[30];
struct ming{char ch;int t,tp;}s[1005];
struct matrix{
int c[125][125];
void clear(){for(int i=0;i<tot;i++)for(int j=0;j<tot;j++)c[i][j]=0;}
matrix operator * (const matrix &rhs)const{
matrix res;res.clear();
for(int i=0;i<tot;i++)
for(int k=0;k<tot;k++)
for(int j=0;j<tot;j++)
res.c[i][j]=add(res.c[i][j],1ll*c[i][k]*rhs.c[k][j]%mo,mo);
return res;
}
void print(){
for(int i=0;i<tot;i++,puts(""))
for(int j=0;j<tot;j++)
printf("%d ",c[i][j]);
puts("");
}
}P,T;
void Add(char ch){for(int i=1;i<=m;i++)if(s[i].ch==ch)b[i]=add(b[i],1,s[i].t);}
void Dec(char ch){for(int i=1;i<=m;i++)if(s[i].ch==ch)b[i]=add(b[i],s[i].t-1,s[i].t);}
int ask(){int res=0;for(int i=1;i<=m;i++)res+=b[i]*s[i].tp;return res;}
void dosaka(int id){
if(id>m){
int S=ask();
for(int i=0;i<26;i++){
if(!vt[i])continue;
Add(i+'A');P.c[S][ask()]++;Dec(i+'A');
}
return ;
}
for(int i=0;i<s[id].t;i++)b[id]=i,dosaka(id+1);
}
void dosaka2(int id){
if(id>m){
bool flag=0;
for(int i=0;i<26;i++)ok[i]=0;
for(int i=1;i<=m;i++)if(!b[i])ok[s[i].ch-'A']=1;
for(int i=0;i<26;i++)if(vt[i]&&!ok[i])flag=1;
if(!flag)ans=add(ans,T.c[0][ask()],mo);return ;
}
for(int i=0;i<s[id].t;i++)b[id]=i,dosaka2(id+1);
}
signed main(){
read(n);read(m);tot=1;
for(int i=1;i<=m;i++)scanf("\\n%c %d",&s[i].ch,&s[i].t);
for(int i=1;i<=m;i++)vt[s[i].ch-'A']=1;
for(int i=1;i<=m;i++)tot*=s[i].t;
s[1].tp=1;for(int i=2;i<=m;i++)s[i].tp=s[i-1].tp*s[i以上是关于[CF107D]Crime Management的主要内容,如果未能解决你的问题,请参考以下文章