题解:AT_arc158_f [ARC158F] Random Radix Sort
首先,我们把
我们观察一下发现,对于每一位的每一个交换,只有最后一步是有意义的,故本质不同的操作序列只有
并且我们可以观察到,这个操作本质上是一种基数排序,以操作的最后一位的第一关键字,倒数第二位为第二关键字……如此类推。
先考虑暴力做法
然后再反推多少个序列满足“每种操作的最后一次操作”的顺序是我们想要的。
我们可以直接设
转移就是:
最终
显然可以矩阵快速幂优化。
注意,可能存在最终操作序列没有用到
这一步的复杂度是
继续考虑怎么求出合法的操作序列。
对于排列相关的计数,容易想到状压 DP。
我们对于本质不同操作计数:
从后往前已经考虑了的数位的集合为
那么我们怎么快速求一个集合
于是直接分讨每一个位置即可,用一个高维前缀和求出转移矩阵。
然后DP计数就是直接按照转移矩阵去做就是简单的了。
故总复杂度
代码
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);++i)
#define per(i,a,b) for(register int i=(a);i>=(b);--i)
#define edge(i,u) for(int i=head[u];i;i=e[i].next)
#define lc (u<<1)
#define rc (u<<1|1)
#define pii pair<int,int>
#define pdd pair<double,double>
#define mp make_pair
#define pb push_back
#define fst first
#define sed second
using namespace std;
const int N=2e5+10,M=1e6+10,inf=1e9,mod=998244353;
int qpow(int a,int b){int res=1;while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}return res;}
int inv(int a){return qpow(a,mod-2);}
void Mul(int&a,int b){a=1ll*a*b%mod;}
void Add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
void Dec(int&a,int b){a-=b;if(a<0)a+=mod;}
int mul(int a,int b){return 1ll*a*b%mod;}
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
struct matrix
{
int f[19][19];
matrix(){memset(f,0,sizeof f);}
void init(){memset(f,0,sizeof f);}
int*operator[](int x){return f[x];}
matrix operator*(const matrix&b)const
{
matrix c;
rep(i,0,18)
rep(k,0,18)
{
int val=f[i][k];
rep(j,0,18)
Add(c.f[i][j],mul(val,b.f[k][j]));
}
return c;
}
};
bool MS;int used;
int n,m,k;
int a[N],b[N];
bitset<N>g[19],h[19];
int val[N][19];
int base[19];
int f[N][19];
bool tr[(1<<18)][19];//i状态转移到j的可行性 (0为可行!1为不行!)
int dp[(1<<18)];
void init()
{
rep(w,1,k)
{
matrix res,trans;
res[0][0]=1;
trans[0][0]=w;
rep(i,1,w)
{
trans[i-1][i]=1;
trans[i][i]=w-i;
}
int b=m;
while(b)
{
if(b&1)res=res*trans;
trans=trans*trans;
b>>=1;
}
base[w]=res[0][w];
}
}
map<int,queue<int>>pos;
int ans;
bool MT;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>k;
init();
rep(i,1,n)
{
cin>>a[i];
pos[a[i]].push(i);
}
rep(i,1,n)
{
cin>>b[i];
int now=b[i];
if(!pos[b[i]].size())
return cout<<0,0;
val[i][k]=pos[b[i]].front();
pos[b[i]].pop();
rep(j,0,k-1)
{
val[i][j]=now%10;
now/=10;
}
}
rep(j,0,k)
rep(i,1,n)
{
g[j][i]=(val[i][j]>=val[i-1][j]);
h[j][i]=(val[i][j]!=val[i-1][j]);//每一段的开头
}
rep(j,0,k)
rep(i,1,n)
if(!g[j][i])
{
int base=0;
rep(l,0,k-1)
if(!h[l][i])base|=(1<<l);
tr[base][j]=1;
}
rep(j,0,k)
{
per(i,(1<<k)-1,0)
if(tr[i][j])
{
rep(l,0,k-1)
if((1<<l)&i)
tr[i^(1<<l)][j]=1;
}
}
dp[0]=1;
rep(i,0,(1<<k)-1)
{
rep(j,0,k-1)
if((tr[i][j]^1)&&!(i&(1<<j)))
dp[i|(1<<j)]+=dp[i];
if(tr[i][k]^1)//可以转移到初始状态,即是合法状态
ans+=dp[i]%mod*base[__builtin_popcount(i)]%mod;
}
cout<<ans%mod<<'\n';
cerr<<"Memory:"<<(&MS-&MT)/1048576.0<<"MB Time:"<<clock()/1000.0<<"s\n";
}