链接: D. Rescue Nibel!
题意:
有 n 栈灯 ,每栈灯在 [ l , r] 时间段内是开着的 ,现在需要选择 k 栈灯 ,使他们能在同一时间内打开 ,问有多少种选择方法 。
思路:
- 如果不考虑重复的情况 , 我们可以算出每个时间点 有多少盏灯亮着 ,然后用一个组合数就可以把答案算出来 ,但这样明显会有很多重复的情况 。 再考虑每栈灯都有一个唯一的起点,可以用起点代表每栈灯 ,对于每个时间点 , 我们只计算开始时间在这个点的灯产生的贡献 ,也就是在这选择的灯必须包含这几栈开始时间在这个点的灯 ,即贡献为 (总的灯里选 k 个) - (起点不在这个点的灯里选 k 个) 。
- 我们可以先离散化一下,再用差分数组 统计每个时间点亮着的灯的数量 。
代码:
#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;
}