CF#672(Div.2) D. Rescue Nibel!(差分,离散化,组合数)

原题链接
题意:
有 n 栈灯 ,每栈灯在 [ l , r] 时间段内是开着的 ,现在需要选择 k 栈灯 ,使他们能在同一时间内打开 ,问有多少种选择方法 。
题解:
先离散化,差分,算出每个点覆盖的区间数量s[i] ,
(s[左端点]++,s[右端点+1]- -)然后对s求前缀和)

并记录这个点含的左端点数量bb[i]。对于每一个点,求组合数的时候,每个方案必须包含至少一个左端点,这样就不会重复。即 C ( s u m [ i ] , k ) − C ( s u m [ i ] − b b [ i ] , k ) C(sum[i],k)-C(sum[i]-bb[i],k) C(sum[i],k)C(sum[i]bb[i],k)
(i 全部点的数量任取k个,减掉全部都不取左端点的数量)

组合数可以用定义求解。 c a b = a ! b ! ∗ ( a − b ) ! c_a^b=\frac{a!}{b!*(a-b)!} cab=b!(ab)!a!
求分母的时候要用逆元。

AC代码 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,mod=998244353;
//N开大一点,不然可能会越界。因为每个点都有一个l和 r
typedef long long ll;
ll n,m,sum[N],val,bb[N];//注意开ll
//sum某个点被覆盖的数量,bb这个点是某区间左端点数量 
ll in[N],inv[N];//in表示阶乘,inv表示逆元
vector<int>alls;
struct node
{
	int l,r;
}a[N];
ll qp(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1)res=res*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return res%mod;
}
int find(int x)
{
	return lower_bound(alls.begin(),alls.end(),x)-alls.begin();
	//找到离散化后对应的下标
}
void ini()
{
	in[0]=inv[0]=1;
	for(int i=1;i<N;i++)in[i]=in[i-1]*i%mod;
	inv[N-1]=qp(in[N-1],mod-2);
	for(int i=N-2;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll c(ll a,ll b)
{
	if(a<b)return 0;
	return (in[a]*inv[b])%mod*inv[a-b]%mod;
}
int main()
{
	cin>>n>>m;
	ini();
	for(int i=0;i<n;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		alls.push_back(a[i].l);
		alls.push_back(a[i].r+1);
	}
	sort(alls.begin(),alls.end());
	alls.erase(unique(alls.begin(),alls.end()),alls.end());
	for(int i=0;i<n;i++)
	{
		int x=find(a[i].l),y=find(a[i].r+1);
		bb[x]++;
		sum[x]++,sum[y]--;
	}
	for(int i=1;i<alls.size();i++)sum[i]+=sum[i-1];//每个端点覆盖数量
	ll ans=0;
	for(int i=0;i<alls.size();i++)
	{
		if(sum[i]<m)continue;
		ans=(ans+c(sum[i],m)-c(sum[i]-bb[i],m)+mod)%mod;//对于某一个点,必须选到左端点是这个点的组合数(每个组合数里面取,至少有一个是左端点) 
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值