【xsy1503】 fountain DP

题目大意:给你$D$个格子,有$n$个喷水器,每个喷水器有一个喷水距离$r_i$。

现在你需要在这$D$个格子中选择$n$个位置按照任意顺序安装这$n$个喷水器,需要满足$n$个喷水器互相喷不到对方。

问方案数,对$10^9+7$取模

数据范围:$n$,$r_i≤40$,$D≤10^5$

 

我们不妨考虑我们钦定了这$n$个喷水器的出现顺序,从左到右第$i$个喷水器编号为$p[i]$。

确定排列顺序后,令$d=\sum \limits_{i=1}^{n-1} max(r_{p[i]},r_{p[i+1]})$

我们发现上式累加的实际上是相邻两个喷水器之间的最小间隔

我们尝试把这个间隔中的格子看成是一个格子。

我们就可以把原序列中$D$个格子看成是$D-d-1$个了。

现在也就是变成了要在这剩下的格子之间插入这$n$个喷水器,方案数显然为$\binom{D-d-1+n}{n}$。

 

下面考虑如何求不同的排列顺序数量。

我们先将$n$个喷水器按照喷水半径进行排序。

设$f[i][j][k]$表示前i个喷水器必须出现,且这$i$个喷水器间(包括两端),有$j$个可以插入喷水器,且由这些喷水器累加出的$d$为$k$的方案数量。

下面考虑在$f[i][j][k]$的基础上插入第$i+1$个喷水器。

假定这个喷水器插入后,两侧不能再插入喷水器,则有$f[i+1][j-1][k+2r_{i+1}]+=f[i][j][k]\times (j-2)$

假定这个喷水器插入后,只有一侧能插入喷水器,则有$f[i+1][j][k+r_{i+1}]+=f[i][j][k]\times (2j-2)$

上面两个转移需要$-2$的原因显然(并不能允许最左侧和最右侧插入喷水器)

假定这个喷水器插入后,两侧皆可以插入喷水器,则有$f[i+1][j+1][k]+=f[i][j][k]\times j$

 

初始情况:$f[1][2][0]=1$,答案为$\sum \limits_{i=1}^{\infty} f[n][2][i]$

转移和答案统计的时候记得取模即可

时间复杂度:$O(n^2\sum \limits_{i=1}^{n} r_i)$

 

 1 #include<bits/stdc++.h>
 2 #define M 42
 3 #define N 110000
 4 #define L long long
 5 #define MOD 1000000007
 6 using namespace std;
 7 
 8 L pow_mod(L x,L k){L ans=1; for(;k;k>>=1,x=x*x%MOD) if(k&1) ans=ans*x%MOD;  return ans;}
 9 L fac[N]={0},invfac[N]={0};
10 L C(int n,int m){if(n-m<0) return 0; return fac[n]*invfac[m]%MOD*invfac[n-m]%MOD;}
11 
12 int n,D,r[M]={0};
13 L f[M][M][M*M]={0};
14 
15 
16 int main(){
17     fac[0]=1; for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%MOD;
18     invfac[N-1]=pow_mod(fac[N-1],MOD-2);
19     for(int i=N-2;~i;i--) invfac[i]=invfac[i+1]*(i+1)%MOD;
20     
21     scanf("%d%d",&n,&D);
22     for(int i=1;i<=n;i++) scanf("%d",r+i);
23     sort(r+1,r+n+1);
24     f[1][2][0]=1;
25     for(int i=1;i<n;i++)
26     for(int j=0;j<=n+2;j++)
27         for(int k=0;k<M*M;k++)
28         if(f[i][j][k]){
29             (f[i+1][j+1][k]+=f[i][j][k]*j)%=MOD;
30             if(j>1) (f[i+1][j-1][k+2*r[i+1]]+=f[i][j][k]*(j-2))%=MOD;
31             if(j>1) (f[i+1][j][k+r[i+1]]+=f[i][j][k]*(2*j-2))%=MOD;
32         }
33     L ans=0;
34     for(int d=0;d<M*M;d++)
35     if(f[n][2][d]){
36         (ans+=C(D-d-1+n,n)*f[n][2][d])%=MOD;
37     }
38     cout<<ans<<endl;
39 }
posted @ 2019-04-09 15:08  AlphaInf  阅读(233)  评论(0编辑  收藏  举报