题目链接:
题目大意:
给出一列灯,初始有一些点亮的灯,每次只能点亮与已经点亮的灯相邻的灯,问点亮所有灯的方案有多少种。
题目分析:
- 首先利用初始已经点亮的灯分段,最左侧和最右侧的两段因为只有一侧有灯,所以单论这一段的话,点亮的方案只有一种。
- 对于中间的段落,因为两侧都有灯,所以每次都有两个灯备选,也就是有 2n 中方案。
- 然后因为各个段的点亮也有交叉的顺序,所以就是对于每一段采取类似于插板的组合方法,也就是
Cnumsum,sum是当期分段的元素总数,num是当前段元素的总数
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAX 1007
using namespace std;
typedef long long LL;
int n,m;
const LL mod = 1e9+7;
LL ans = 1;
int mark[MAX];
LL c[MAX][MAX];
LL qp ( LL n )
{
LL ret = 1LL, num = 2LL;
while ( n )
{
if ( n&1 )
{
ret *= num;
ret %= mod;
}
num *= num;
num %= mod;
n >>= 1;
}
return ret;
}
void init ( )
{
c[0][0] = c[1][0] = c[1][1] = 1LL;
for ( int i = 2 ; i < MAX ; i++ )
for ( int j = 0 ; j <= i ; j++ )
if ( j == 0 || j == n ) c[i][j] = 1LL;
else
{
c[i][j] = c[i-1][j-1] + c[i-1][j];
c[i][j] %= mod;
}
}
int main ( )
{
init ( );
while ( ~scanf ( "%d%d" , &n , &m ) )
{
ans = 1LL;
memset ( mark , 0 , sizeof ( mark ) );
while ( m-- )
{
int x;
scanf ( "%d" , &x );
mark[x] = 1;
}
LL num = 0,sum = 0,temp = 0;
int l=1,r=n;
while ( !mark[l] ) l++,sum++;
while ( !mark[r] ) r--,temp++;
//cout << l << " " << r << endl;
for ( int i = l+1 ; i <= r ; i++ )
{
if ( mark[i] )
{
if ( num == 0 ) continue;
ans *= qp ( num-1 );
ans %= mod;
ans *= c[sum][num];
ans %= mod;
num = 0;
}
else sum++,num++;
}
ans *= c[sum+temp][temp];
ans %= mod;
printf ( "%lld\n" , ans );
}
}