codeforces 1558 D. Top-Notch Insertions (线段树+组合)
题目链接:http://codeforces.com/contest/1558/problem/D
如果没有插入的条件限制,那么对于所有元素都有 \(a[i] <= a[i+1]\)
每次插入相当于确定了一个小于关系,将 \(x\) 位置的元素插入到 \(y\) 位置,相当于 \(a[x] < a[y]\)
所以假设我们已经知道有 \(c\) 个 ‘\(<\)’ 关系,设排列后的序列为 \(b\),则有 \(c\) 个位置满足 \(b[i]<b[i+1]\),有 \(n-1-c\) 个位置满足 \(b[i]<=b[i+1]\)(其中每个位置是什么关系已知),求有多少种序列 \(b\)
答案为 \(C_{2*n-1-c}^n\),考虑对于 \(n-1-c\) 个满足相等关系的位置,将 \(b[i+1],\dots,b[n]\) 这些元素全部 \(+1\),那么最大可能的元素为 \(n+n-1-c\),也即这一序列与在 \(2*n-1-c\) 的排列中选出 \(n\) 个数构成了双射
如果将插入操作顺序反过来,逆向操作,那么每次插入的位置 \(y\) 的意义就是当前所有未填位置中第 \(y\) 大的位置 \(p\)(初始未填位置为 \(1\) 到 \(n\)),而若当前第 \(y+1\) 大位置为 \(q\),那么 \(p<q\),如果没有其他元素 \(w\) 与 \(q\) 满足 \(w<q\),则 \(c++\),否则 \(c\) 不变,\(w\) 与 \(p\) 满足 \(w<=p\),找到未被选择的第 \(k\) 个位置可以使用线段树
题目中说明不保证所有数据中 \(n\) 的和的范围,那么考虑到每次最多操作 \(m\) 次,记录操作的位置,最后将操作撤回即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200010;
const int M = 998244353;
int T, n, m, c;
int l[maxn], r[maxn], vis[maxn];
int fac[2*maxn], ifac[2*maxn];
struct Node{
int sum;
}t[maxn<<2];
void pushup(int i){
t[i].sum = t[i<<1].sum + t[i<<1|1].sum;
}
void build(int i, int l, int r){
if(l == r) {
t[i].sum = 1;
return;
}
int mid = (l + r) >> 1;
build(i<<1, l, mid); build(i<<1|1, mid + 1, r);
pushup(i);
}
void modify(int i, int p, int v, int l, int r){
if(l == r){
t[i].sum = v;
return;
}
int mid = (l + r) >> 1;
if(p <= mid) modify(i<<1, p, v, l, mid);
else modify(i<<1|1, p, v, mid + 1, r);
pushup(i);
}
int query(int i, int k, int l, int r){
if(l == r){
return l;
}
int mid = (l + r) >> 1;
if(t[i<<1].sum >= k) return query(i<<1, k, l, mid);
else return query(i<<1|1, k-t[i<<1].sum, mid + 1, r);
}
int qsm(int i, int po){
int res = 1;
while(po){
if(po & 1) res = 1ll * res * i % M;
po >>= 1;
i = 1ll * i * i % M;
}
return res;
}
vector<int> vp;
vector<int> vq;
int C(int x, int y){
return 1ll * fac[x] * ifac[y] % M * ifac[x-y] % M;
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
T = read();
fac[0] = 1;
for(int i = 1 ; i <= 400000 ; ++i) fac[i] = 1ll * fac[i-1] * i % M;
ifac[400000] = qsm(fac[400000], M - 2);
for(int i = 399999 ; i >= 0 ; --i) ifac[i] = 1ll * ifac[i+1] * (i+1) % M;
build(1, 1, 200000);
while(T--){
vp.clear(); vq.clear();
c = 0;
n = read(), m = read();
for(int i = 1 ; i <= m ; ++i){
l[i] = read(), r[i] = read();
}
for(int i = m ; i >= 1 ; --i){
int p = query(1, r[i], 1, 200000);
int q = query(1, r[i]+1, 1, 200000);
vp.push_back(p);
vq.push_back(q);
modify(1, p, 0, 1, 200000);
if(!vis[q]) {
++c;
vis[q]++;
}
}
printf("%d\n", C(2*n-c-1, n));
for(auto u : vp) modify(1, u, 1, 1, 200000);
for(auto u : vq) vis[u] = 0;
}
return 0;
}