CF1648 C. Tyler and Strings (组合数学,线段树) (1900)
https://codeforces.com/problemset/problem/1648/C
题意: 给出两个数列A和B。给A重新排序使得A的字典序小于B。
思路:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define int long long
#define PII pair<int, int>
const int N = 3e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
ll F[N], inv[N];
int s[N], t[N], cnt[N];
ll sum_i[N];
struct node {
ll val, lazy;
}tr[N << 2];
ll qmi ( ll m, ll k ) {
ll res = 1 % mod;
while(k) {
if(k & 1) res = res * m % mod;
m = m * m % mod;
k >>= 1;
}
return res;
}
void init(int n) {
inv[0] = F[0] = 1;
for ( int i = 1; i <= n; ++ i ) F[i] = F[i - 1] * i % mod;
inv[n] = qmi(F[n], mod - 2);
for ( int i = n - 1; i >= 1; -- i ) inv[i] = inv[i + 1] * (i + 1) % mod;
}
void pushup(int rt) {
tr[rt].val = (tr[rt << 1].val + tr[rt << 1 | 1].val) % mod;
}
void push_down(int l, int r, int rt) {
if(tr[rt].lazy == 1) return;
tr[rt << 1].lazy = tr[rt << 1].lazy * tr[rt].lazy % mod;
tr[rt << 1 | 1].lazy = tr[rt << 1 | 1].lazy * tr[rt].lazy % mod;
tr[rt << 1].val = tr[rt << 1].val * tr[rt].lazy % mod;
tr[rt << 1 | 1].val = tr[rt << 1 | 1].val * tr[rt].lazy % mod;
tr[rt].lazy = 1;
}
void build(int l, int r, int rt) {
tr[rt].lazy = 1;
if(l == r) {
tr[rt].val = sum_i[l] % mod;
return;
}
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
pushup(rt);
}
ll query( int a, int b, int l, int r, int rt ) {
if( b < l || a > r ) return 0;
if( a <= l && b >= r ) {
return tr[rt].val;
}
int mid = l + r >> 1;
push_down(l, r, rt);
return (query(a, b, l, mid, rt << 1) +
query(a, b, mid + 1, r, rt << 1 | 1)) % mod;
}
void mul(int a, int b, ll val, int l, int r, int rt) {
if( b < l || a > r ) return;
if( a <= l && b >= r ) {
tr[rt].val %= mod;
tr[rt].val = tr[rt].val * val % mod;
tr[rt].lazy %= mod;
if(!tr[rt].lazy) tr[rt].lazy = 1;
tr[rt].lazy = tr[rt].lazy * val % mod;
return;
}
int mid = l + r >> 1;
push_down(l ,r, rt);
mul(a, b, val, l, mid, rt << 1);
mul(a, b, val, mid + 1, r, rt << 1 | 1);
pushup(rt);
}
signed main() {
IOS
init(200003);
int n, m; cin >> n >> m;
for ( int i = 1; i <= n; ++ i ) cin >> s[i], ++ cnt[s[i]];
for ( int i = 1; i <= m; ++ i ) cin >> t[i];
ll sum = F[n - 1];
for ( int i = 1; i <= 200000; ++ i ) if(!cnt[i]) continue; else sum = sum * inv[cnt[i]] % mod;
for ( int i = 1; i <= 200000; ++ i ) if(!cnt[i]) continue; else sum_i[i] = sum * cnt[i] % mod;
build(1, 200000, 1);
bool ok = 0; ll ans = 0, allcnt = n - 1;
int mm = min(n , m);
for ( int i = 1; i <= mm; ++ i ) {
if(t[i] != 1) ans = (ans + query(1, t[i] - 1, 1, 200000, 1)%mod) % mod;
if(i == mm) { ok = 1; break; }
if(!cnt[t[i]]) break;
mul(1, 200000, cnt[t[i]] * qmi(allcnt, mod - 2) % mod, 1, 200000, 1);
mul(t[i], t[i], (cnt[t[i]] - 1) * qmi(cnt[t[i]], mod - 2) % mod, 1, 200000, 1);
-- allcnt; -- cnt[t[i]];
}
if(ok && n < m) ans = (ans + query(t[n], t[n], 1, 200000, 1)% mod) % mod;
cout << ans << '\n';
return 0;
}