CF1558D Top-Notch Insertions

https://www.luogu.com.cn/problem/CF1558D

VP到这题卡住,吃了个饭以为懂了,结果回来发现看错题。。

不过考场上也不一定想得出来

先假设每个数前面的那个符号是 < = <= <=,如果有一个数插到它前面,它前面的符号就会变成 < < <(感性理解一下)

然后假设最后有 c c c < < <,那么就有 n − 1 − c n-1-c n1c < = <= <=
根据基础的组合数学知识,当有 < = <= <=时可以转换成加上一个编号 < + ? <+? <+?,这样值域就变成了 n + n − 1 − c n+n-1-c n+n1c
从中选值,就可以得到最终的答案为
( 2 n − 1 − c n ) \large \binom{2n-1-c}{n} (n2n1c)
现在考虑如何计算c

不论什么时候最前面一段一定是单调的
一开始先全部加进去
考虑从后往前删除(插入反过来),假设操作为 ( p , q ) (p, q) (p,q)
只需找到剩下的当中第 q q q大的和第 q + 1 q+1 q+1大的的, p p p是插到了 q q q这个位置,所以 q + 1 q+1 q+1前面的符号变成了 < < <
然后再 p p p删掉
用一颗权值线段树维护即可

注意数据范围限制,处理一下细节问题即可
code:

#include<bits/stdc++.h>
#define N 400050
#define mod 998244353
#define ll long long
using namespace std;
int qpow(ll x, int y) {
    ll ret = 1;
    for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
    return ret;
}
int fac[N], ifac[N], size[N << 2], x[N], y[N], n, m, t, vis[N], sta[N];
void init(int n) {
    fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = 1ll * fac[i - 1] * i % mod;
    ifac[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i --) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
}
int C(int x, int y) {
    if(x < y) return 0;
    return 1ll * fac[x] * ifac[y] % mod * ifac[x - y] % mod;
}
void build(int rt, int l, int r) {
    size[rt] = r - l + 1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid), build(rt << 1 | 1, mid + 1, r);
}
void update(int rt) {
    size[rt] = size[rt << 1] + size[rt << 1 | 1];
}
void add(int rt, int l, int r, int x, int o) {
    if(l == r) {
        size[rt] += o;
        return ;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) add(rt << 1, l, mid, x, o);
    else add(rt << 1 | 1, mid + 1, r, x, o);
    update(rt);
}
int query(int rt, int l, int r, int k) {
    if(l == r) return l;
    int mid = (l + r) >> 1;
    if(size[rt << 1] >= k) return query(rt << 1, l, mid, k);
    else return query(rt << 1 | 1, mid + 1, r, k - size[rt << 1]);
}
set<int> S;
int main() {
    int lim = 2e5;
    init(N - 20); build(1, 1, lim);
    scanf("%d", &t);
    while(t --) {
        scanf("%d%d", &n, &m);
        int top = 0; S.clear();
        for(int i = 1; i <= m; i ++) scanf("%d%d", &x[i], &y[i]);
        for(int i = m; i >= 1; i --) {
            int p = query(1, 1, lim, y[i]), q = query(1, 1, lim, y[i] + 1);
            S.insert(q); add(1, 1, lim, p, -1);
            sta[++ top] = p;
        }
        int ans = S.size();
        for(int i = 1; i <= top; i ++) add(1, 1, lim, sta[i], 1);
        printf("%d\n", C(2 * n - 1 - ans, n));
    }
    return 0;
}
posted @ 2021-08-31 14:19  lahlah  阅读(30)  评论(0编辑  收藏  举报