Codeforces 1420 D 离散化 + 排序 + 树状数组

本文介绍了如何利用离散化和树状数组来解决Cnm组合问题,避免了1e9数据规模的挑战。通过将区间离散化,并维护树状数组,可以有效地找到区间交集并计算组合数。文章还提到,通过巧妙地更新组合数,避免了直接计算组合数导致的时间消耗。最后,提供了实现该算法的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       昨天看到题本来就能秒出的,结果组合式公式套错了。。。

Cnm=PnmPmm=n!m!(n−m)!C_n^m=\frac{P_n^m}{P_m^m}=\frac{n!}{m!(n-m)!}Cnm=PmmPnm=m!(nm)!n!

       我们考虑先离散化 lll, rrr,因为如果我们想用树状数组维护的话,1e91e91e9 的数据显然是不可接受的,相比之下 2∗3e52*3e523e5 的规模的数,是可以维护的。

       我们考虑将当前区间的右端插入树状数组,然后找区间左端的右边存在多少之前插入的区间右端,这些就是与当前区间有交集的存在。

       然后我们就只考虑插入当前区间能构造多少种组合,也就是考虑之前插入的与当前区间有交集的数选取 k−1k-1k1 个能有多少种组合,把所有结果加起来就是答案。

       除了以上大体思路之外就是一些小 tricktricktrick, 我们不去暴力计算组合数,而是维护一个当前的组合数方案,当总数改变时,它也跟着改变,比如当 Cnm−>Cn+1mC_n^m->C_{n+1}^mCnm>Cn+1m,也就是 Cnm∗(n+1)/(n+1−m)C_n^m*(n+1)/(n+1-m)Cnm(n+1)/(n+1m),这里除法用一下逆元。这样显然能大大节省时间。

       要是昨晚组合数公式没搞错现在已经1900分了(#`O′)

       代码如下:

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
const int maxn = 6e5 + 5;
const int mod = 998244353;
int a[maxn];
int tree[maxn];
int cnt;
pair<int, int> q[maxn];

inline int powmod(LL a, int b){
    LL ans = 1;
    while(b){
        if(b & 1) ans = a * ans % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

inline int lowbit(int x){return x & (-x);}

inline void add(int x){
    while(x <= cnt){
        tree[x]++;
        x += lowbit(x);
    }
}

inline int sum(int x){
    int tol = 0;
    while(x){
        tol += tree[x];
        x -= lowbit(x);
    }
    return tol;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, k, tol, last;
    LL ans = 0;
    LL now = 1;
    cin >> n >> k;
    ans = last = cnt = 0;
    for(int i = 1; i <= n; i++){
        cin >> q[i].first >> q[i].second;
        a[++cnt] = q[i].first;
        a[++cnt] = q[i].second;
    }
    sort(a + 1, a + 1 + cnt);
    sort(q + 1, q + 1 + n);
    cnt = unique(a + 1, a + 1 + cnt) - a - 1;
    for(int i = 1; i <= n; i++){
        tol = (i - 1) - sum(lower_bound(a + 1, a + 1 + cnt, q[i].first) - a - 1);
        add(lower_bound(a + 1, a + 1 + cnt, q[i].second) - a);
        if(tol > k - 1){
            if(tol == last + 1)
                now = now * tol % mod * powmod(tol - k + 1, mod - 2) % mod;
            else{
                while(last > tol){
                    now = now * powmod(last, mod - 2) % mod * (last - k + 1) % mod;
                    last--;
                }
            }
            ans = (ans + now) % mod;

        }else if(tol == k - 1){
            now = 1;
            ans++;
        }
        last = tol;
    }
    cout << ans % mod << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值