题解:P12467 『FCRT / 1 - 4』Century
可以先看一下这个:https://oi-wiki.org/dp/number/
直接开正解。
我们令
转移见下:
int kk=k,limm=llim&(num==lim2);
if((kk&(1<<(y-1)))&&(num!=lim1)){
kk^=(1<<(y-1));
}
f[x][y][kk][limm]=(f[x][y][kk][limm]+1ll*maxn*f[i][j][k][lim]%mod)%mod;
其中,
此时的复杂度可以做到
加上滚动数组优化会得到
考虑优化,可以发现不同的
这一部分代码如下,其中
if(maxn!=0){
int num=0;
int kk=k,limm=llim&(num==lim2);
if((kk&(1<<(y-1)))&&(num!=lim1)){
kk^=(1<<(y-1));
}
f[x&1][y][kk][limm]=(f[x&1][y][kk][limm]+1ll*maxn*f[i&1][j][k][lim]%mod)%mod;
}
int num=maxn;
int kk=k,limm=llim&(num==lim2);
if((kk&(1<<(y-1)))&&(num!=lim1)){
kk^=(1<<(y-1));
}
f[x&1][y][kk][limm]=(f[x&1][y][kk][limm]+f[i&1][j][k][lim])%mod;
此时时间复杂度为
完整代码:
#include<bits/stdc++.h>
using namespace std;
// #define int long long
#define N 19
const int mod=998244353;
int n,m,r[N][N],c[N][N],f[2][N][1<<18][2];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
long long tmp,tot=m;
cin>>tmp;
while(tmp){
r[i][tot--]=tmp%10;
tmp/=10;
}
}
for(int i=1;i<=m;i++){
long long tmp,tot=n;
cin>>tmp;
while(tmp){
c[tot--][i]=tmp%10;
tmp/=10;
}
}
f[0][m][(1<<m)-1][1]=1;
for(int i=0;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<(1<<m);k++){
f[1^(i&1)][j][k][0]=0,f[1^(i&1)][j][k][1]=0;
}
}
for(int j=1;j<=m;j++){
if(i==0&&j!=m){
continue;
}
for(int k=0;k<(1<<m);k++){
for(int lim=0;lim<=1;lim++){
int x=i,y=j+1;
if(y==m+1){
x++;
y=1;
}
int lim1=(k&(1<<(y-1)))?c[x][y]:9;
int lim2=lim?r[x][y]:9;
int llim=lim;
if(y==1){
lim2=r[x][y];
llim=1;
}
int maxn=min(lim1,lim2);
if(maxn!=0){
int num=0;
int kk=k,limm=llim&(num==lim2);
if((kk&(1<<(y-1)))&&(num!=lim1)){
kk^=(1<<(y-1));
}
f[x&1][y][kk][limm]=(f[x&1][y][kk][limm]+1ll*maxn*f[i&1][j][k][lim]%mod)%mod;
}
int num=maxn;
int kk=k,limm=llim&(num==lim2);
if((kk&(1<<(y-1)))&&(num!=lim1)){
kk^=(1<<(y-1));
}
f[x&1][y][kk][limm]=(f[x&1][y][kk][limm]+f[i&1][j][k][lim])%mod;
}
}
}
}
int ans=0;
for(int k=0;k<(1<<m);k++){
for(int lim=0;lim<=1;lim++){
ans=(ans+f[n&1][m][k][lim])%mod;
}
}
cout<<ans;
}