Loading

题解 【CF294C Shaass and Lights】

\(\Large\texttt{CF294C}\)

吐槽一句:真的是恶评,还有数据范围完全可以开到 \(1e5\) 的。

做法:推式子

题意

不作赘述。

思路

  • 首先分别计算对于所有灯将这条线段所围成的每条线段的点灯方法,因为每条线段上要点的灯的个数一定,但是可以由左端点向右也可以从右端点向左(除了起始和结尾的两条线段),点灯的交错重复构成了一段序列。

    则我们要求的便可以简化为:在一段序列上涂色,两种颜色,则涂色方案便是 \(2^{len}\)

    但是这是错的,当两边的灯点到只剩一个灯时,点下一个灯对于从左还是从右来说都是一样的,所以方案是 \(2^{len-1}\)

  • (其实这是个经典问题)因为每段线段点灯的步骤互不干扰。

    先考虑两段区间 \(A\)\(B\) 一共的点亮方案,一种点亮 \(A\) 区间的方案可以对应一种点亮 \(B\) 区间的方案,首先的方案数便是 \(calc_A\times calc_B\) ,其次,合起来的每一种点亮方案可以是 \(A\)\(B\) 区间交织起来,并且是任意交织,所以合起来每一种方案对应了 \(C(len_A+len_B,len_A)\) 种方案,相乘便是两个区间一起点亮的方案数,扩展到第三个区间也是如此。

预处理出阶乘和逆元,通过 \(\texttt{O(1)}\) 算组合数。

时间复杂度 \(\texttt{O(NlogN)}\) (排序复杂度)。

注意特判开头的区间和结尾的区间,还有两盏灯之间没有灯可以点亮的情况。

代码

#include <bits/stdc++.h>
using namespace std;

 #define int long long
 const int N = 1000;
 const int mod = 1e9+7;
inline int read()
{
    int s = 0;
    register bool neg = 0;
    register char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        neg |= (c == '-');
    for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
        ;
    s = (neg ? -s : s);
    return s;
}

int a,b,fac[N+10],inv[N+10],power[N+10],s[N+10];

inline void init() {
	fac[0]=fac[1]=inv[0]=inv[1]=power[0]=1;
	for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=N;i++) inv[i]=inv[i-1]*inv[i]%mod,power[i]=power[i-1]*2%mod;
}

inline int C(int n,int m) {
	return fac[n]*inv[n-m]%mod*inv[m]%mod;
}

signed main()
{
//	freopen("in.txt","r",stdin);
	init();
	a=read();
	b=read();
	int pre=0,sum=0,ans=1,x=0;
	for(int i=1;i<=b;i++) s[i]=read();
	sort(s+1,s+b+1);
	for(int i=1;i<=b;i++) {
		x=s[i];
		sum+=x-pre-1;
		if(pre!=0&&pre<x-1) ans=(ans*C(sum,x-pre-1)%mod*(power[x-pre-2]))%mod;
		pre=x;
	}
	sum+=a-pre;
	ans=(ans*C(sum,a-x))%mod;
	printf("%lld",ans);
    return 0;
}
posted @ 2020-12-02 21:02  RedreamMer  阅读(133)  评论(0编辑  收藏  举报