题解:P13547 [OOI 2022] Third grader's task
Circle_Table · · 题解
题目传送门
欢迎来博客园阅读!
题意简述
有
思路
先只考虑排列数。
对于字典序,当一个地方的字母出现不同之后,就无所谓后面的字符是什么样子了。于是,对于每一个位置
- 选出
s_i=t_i ,则接下来继续考虑后面的情况; - 选出
s_i<t_i ,则后面的字符可以任意组合。
也就是说,当我们到达位置
- 令
s_i=t_i ,则s 中不剩t_i 了就结束,s 中还剩t_i 则使用一个,更新x \leftarrow x \times cnt_{t_i},cnt_{t_i} \leftarrow cnt_{t_i} - 1 ; -
问题在于
还有一种特殊情况:
-
n<m - 不存在因为
s 中不剩t_i 了而结束的情况
最后处理重复的部分:
先只考虑排列数。
显然重复的部分就是把排列数变成组合数,因为很多字符都是相同的。在输入的时候同时维护一个桶排数组
于是本题完成。
代码
#include <bits/stdc++.h>
#define loop(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define rloop(i,a,b) for(int i=(a);i>=(int)(b);i--)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=998244353;
int n,m;
int s,t[N];
ll fac[N],inv[N];
int tr[N],p[N]; // p[i]:i 的出现次数
ll qmi(ll a,int k){
ll res=1;
while(k){
if(k&1)res=res*a%mod;
a=a*a%mod;
k>>=1;
}
return res;
}
void init(){ // 预处理阶乘及其逆元
fac[0]=1;
loop(i,1,200000)fac[i]=fac[i-1]*i%mod;
inv[200000]=qmi(fac[200000],mod-2);
rloop(i,199999,0)inv[i]=inv[i+1]*(i+1)%mod;
return;
}
// 树状数组部分
void add(int u,int d){
while(u<=200000)tr[u]+=d,u+=lowbit(u);
return;
}
int sum(int u){
int res=0;
while(u>0)res+=tr[u],u-=lowbit(u);
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
loop(i,1,n)cin>>s,add(s,1),p[s]++;
loop(i,1,m)cin>>t[i];
init();
ll ans=0,x=1;
bool flag=n<m; // flag = 1 : s 为 t 的前缀
loop(i,1,min(n,m)){
ans=(ans+x*sum(t[i]-1)%mod*fac[n-i]%mod)%mod;
if(sum(t[i])-sum(t[i]-1)==0){
flag=0;
break;
}
x=x*(sum(t[i])-sum(t[i]-1))%mod;
add(t[i],-1);
}
if(flag)ans=(ans+x)%mod;
loop(i,1,200000)ans=ans*inv[p[i]]%mod;
cout<<ans<<'\n';
return 0;
}
完结撒花花!