CodeForces - 294C_Shaass and Lights(组合数学)

CodeForces - 294C_Shaass and Lights

There are n n n lights aligned in a row. These lights are numbered 1 to n n n from left to right. Initially some of the lights are switched on. Shaass wants to switch all the lights on. At each step he can switch a light on (this light should be switched off at that moment) if there’s at least one adjacent light which is already switched on.

He knows the initial state of lights and he’s wondering how many different ways there exist to switch all the lights on. Please find the required number of ways modulo 1000000007 ( 1 0 9 10^9 109 + 7).

Input

The first line of the input contains two integers n and m where n is the number of lights in the sequence and m is the number of lights which are initially switched on, (1 ≤ n ≤ 1000, 1 ≤ m ≤ n). The second line contains m distinct integers, each between 1 to n inclusive, denoting the indices of lights which are initially switched on.

Output

In the only line of the output print the number of different possible ways to switch on all the lights modulo 1000000007 ( 1 0 9 10^9 109 + 7).

题意:

墙上有一排灯,个数为n,其中有m个灯已经亮了,其余灯都是熄灭的。你想要将所有灯都点亮,一盏灯如果要被点亮,那么它相邻的必须要一盏灯已经被点亮,问你将所有灯都点亮所需要的方案数。

思路:

一开始我们考虑一段区间中其头和尾是亮着,但中间都是熄灭情况的方案数,我们可以发现我们每一次都可以取2个头或尾,除非只剩下一个的时候,只能取1,因此这一段方案数为 2 l e n − 1 2^{len-1} 2len1

然后我们在考虑只有一端是亮着的情况,只有一端亮着的情况那么就是1,因为你1次只能取1个。

这道题就是总共有n-m的灯是灭的,然后让你去求剩余还熄灭的灯里面去填连续的一段熄灭的灯,这个方案数乘以这一段的方案数之和就是答案。

PS:要将其给定的数组排序,它给定的数组是乱序的,同时注意len为0的时候

代码

#include<bits/stdc++.h>
#define Case int t;scanf("%d",&t);while(t--)
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int M=1e6+10;
const int mod=1e9+7;
ll d[N];
ll a[N];
ll fac[N];
ll inv[N];
ll qpow(ll q,ll n){
	ll res=1;
	while(n){
		if(n&1)res=(res*q)%mod;
		n>>=1;
		q=(q*q)%mod;
	}
	return res;
}
void run(){
	ll n,m;
	scanf("%lld%lld",&n,&m);

	for(int i=1;i<=m;i++){
		scanf("%lld",&a[i]);
	}
	if(n==m){
		printf("1\n");
		return ;
	}
	sort(a+1,a+1+m);
	a[0]=0;
	a[m+1]=n+1;
	ll len=0;
	for(int i=1;i<=m+1;i++){
		d[i]=a[i]-a[i-1]-1;
		len+=d[i];
	}
	ll sum=1;
	for(int i=1;i<=m+1;i++){
		if(i==1){
			sum=(sum*fac[len]%mod*inv[len-d[i]]%mod*inv[d[i]]%mod)%mod;
			len-=d[i];
		}
		else if(i==m+1){
			sum=(sum*fac[len]%mod*inv[len-d[i]]%mod*inv[d[i]]%mod)%mod;
			len-=d[i];
		}
		else {
			if(d[i]!=0){
				sum=(sum*fac[len]%mod*inv[len-d[i]]%mod*inv[d[i]]%mod*qpow(2,d[i]-1)%mod)%mod;
			}
			len-=d[i];
		}
	}
	printf("%lld\n",sum);
}
int main(){
	fac[0]=1;
	for(ll i=1;i<=2000;i++){
		fac[i]=fac[i-1]*i%mod;
	}
	inv[2000]=qpow(fac[2000],mod-2);
	for(int i=1999;i>=0;i--){
		inv[i]=(inv[i+1]*(i+1))%mod;
	}
	run();
	return 0;
}

道阻且长,且行且珍惜

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值