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
", 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;
}
原文地址:https://www.cnblogs.com/tuchen/p/15191914.html