Codeforces1648C. Tyler and Strings
传送门
题目大意
给定一个长为 \(n(1\leq n\leq 2\times10^5)\) 的整数序列 \(s(1\leq s_{i}\leq2\times10^5)\) ,以及一个长为 \(m(1\leq m\leq 2\times10^5)\) 的整数序列 \(t(1\leq t_{i}\leq2\times10^5)\) ,求有多少种 \(s\) 的排列,其字典序小于 \(t\) ,答案对 \(998244353\) 取模。
思路
如果字典序要小于 \(t\) ,那么要与 \(t\) 有一个相同的前缀,我们可以枚举这个相同前缀的长度。当长度为 \(i-1\) 时,第 \(i\) 个数字可以是 \(s\) 中剩下的小于 \(t_{i}\) 的所有数字,然后再乘以后面部分的全排列,设 \(s\) 总共有 \(k\) 种不同的数字,每种数字 \(j\) 在相同前缀长度为 \(i-1\) 时剩余个数为 \(cnt_j\) ,对于第 \(i\) 为每个合法的数字种类 \(j\) ,其贡献就为 \(\frac{(n-i)!}{cnt_1!cnt_2!...(cnt_j-1)!...cnt_k!}=\frac{(n-i)!}{cnt_1!cnt_2!...cnt_k!}cnt_j\) ,于是我们可以记所有在第 \(i\) 个数字处可以枚举的数字个数和为 \(now\) ,于是长度为 \(i-1\) 的相同长度的总贡献为 \(\frac{(n-i)!}{cnt_1!cnt_2!...cnt_k!}now\) , \(now\) 我们可以用树状数组维护 \(cnt\) 来求得,一开使可以先预处理出分母,之后的都可以推出。此外还需要特判 \(s\) 整体已经是 \(t\) 前缀的这一个可能答案,最后将贡献相加即可。
代码
#include
#include
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 100000000;
const LL mod = 998244353;
const int maxn = 200010;
LL N, M, S[maxn], T[maxn], cnt[maxn], fact[maxn], invfact[maxn], dat[maxn], n;
void add(LL i, LL x)
{
while (i <= n)
{
dat[i] += x;
i += i & (-i);
}
}
LL sum(LL i)
{
LL ans = 0;
while (i)
{
ans += dat[i];
i -= i & (-i);
}
return ans;
}
LL qpow(LL a, LL x, LL m)
{
LL ans = 1;
while (x)
{
if (x & 1)
ans = ans * a % m;
a = a * a % m;
x >>= 1;
}
return ans;
}
void fact_init(LL n, LL m)
{
fact[0] = fact[1] = 1;
for (LL i = 2; i <= n; i++)
fact[i] = fact[i - 1] * i % m;
invfact[n] = qpow(fact[n], m - 2, m);;
for (LL i = n; i > 0; i--)
invfact[i - 1] = invfact[i] * i % m;
}
void solve()
{
LL ans = 0, exans = 1;
LL allInv = 1;
for (LL i = 1; i <= 200000; i++)
{
if (cnt[i])
allInv = allInv * invfact[cnt[i]] % mod;
}
LL mi = min(N, M);
if (N >= M)
exans = 0;
for (LL i = 1; i <= mi; i++)
{
LL now = sum(T[i] - 1);
ans = (ans + now * fact[N - i] % mod * allInv % mod) % mod;
if (!cnt[T[i]])
{
exans = 0;
break;
}
allInv = (allInv * cnt[T[i]]) % mod;
cnt[T[i]]--;
add(T[i], -1);
}
cout << (ans + exans) % mod << endl;
}
int main()
{
IOS;
n = 200000;
fact_init(200005, mod);
cin >> N >> M;
for (int i = 1; i <= N; i++)
{
cin >> S[i];
cnt[S[i]]++;
add(S[i], 1);
}
for (int i = 1; i <= M; i++)
cin >> T[i];
solve();
return 0;
}