当前位置: 代码迷 >> 综合 >> Codeforces 1420 D. Rescue Nibel! (差分 + 组合数)
  详细解决方案

Codeforces 1420 D. Rescue Nibel! (差分 + 组合数)

热度:96   发布时间:2024-02-22 11:31:21.0

链接: D. Rescue Nibel!

题意:
有 n 栈灯 ,每栈灯在 [ l , r] 时间段内是开着的 ,现在需要选择 k 栈灯 ,使他们能在同一时间内打开 ,问有多少种选择方法 。
思路:

  1. 如果不考虑重复的情况 , 我们可以算出每个时间点 有多少盏灯亮着 ,然后用一个组合数就可以把答案算出来 ,但这样明显会有很多重复的情况 。 所以对于每个时间的点 , 我们只算开始时间在这个点的灯的贡献 , 所以需要在减去 开始时间都不在这个点的贡献 。
  2. 我们可以先离散化一下,再用差分数组 统计每个时间点亮着的灯的数量 。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+7;
const int mod = 998244353;
ll  a1[maxn],b1[maxn];
struct node{
    ll l,r;
}num[maxn];
ll n,k,val[maxn],cnt;
ll ans , d[maxn] , x[maxn] , c[maxn];
ll quickpow(ll a ,ll b){
    ll ans=1;while(b>0){
    if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans;
}
ll ni(ll x){
    return quickpow(x,mod-2);
}
void init (){
    a1[0]=1;for(int i = 1 ;i < maxn ;i ++){
    a1[i] = (a1[i-1] * i) % mod;}b1[maxn - 1] = ni(a1[maxn - 1]);for(int i = maxn - 2 ;i >= 0 ;i --){
    b1[i] = b1[i+1] * (i + 1) % mod;}
}
ll C(ll n,ll m){
    if(n < m) return 0;return (a1[n] * b1[m] % mod) * b1[n-m] % mod;
}
int main() {
    scanf("%lld%lld",&n,&k);init();for(int i = 1; i <= n; i ++){
    scanf("%lld%lld",&num[i].l,&num[i].r);val[cnt ++] = num[i].l;val[cnt ++] = num[i].r;}sort(val,val + cnt);cnt = unique(val,val + cnt) - val;for(int i = 1; i <= n; i ++){
    num[i].l = lower_bound(val , val + cnt , num[i].l) - val + 1;num[i].r = lower_bound(val , val + cnt , num[i].r) - val + 1;//printf ("%lld %lld %lld\n",num[i].l,num[i].r,cnt);}for(int i = 1; i <= n; i ++){
    x[num[i].l] ++;d[num[i].l] ++;d[num[i].r + 1] -- ;}for(int i = 1; i <= cnt; i ++){
    c[i] = c[i - 1] + d[i];}for(int i = 1; i <= cnt; i ++){
    if(c[i] < k) continue;//printf ("%lld %lld ",C(c[i] , k),C(c[i] - x[i] , k));ans = (ans + C(c[i] , k) - C(c[i] - x[i] , k)  + mod) % mod;}printf ("%lld\n",ans);return 0;
}