您的位置:首页 > 其它

[DP] ZROI 2017 提高3 T3 建筑

2017-10-07 14:58 399 查看
首先我们可以简化一下条件,显然只要满足相邻元素满足限制即可。即对于任意的相邻的 i,j 满足 max{hi,hj}>=|xi−xj|

考虑如果我们已经知道排列的顺序,那么我们很容易统计这种顺序下的答案:

设 S=∑n−1i=1max{hi,hi+1} ,则相当于选 n 个非负整数,加和不大于 X−1−S 的方案数。经典的隔板法得 方案数 (X−1−S+nn) 。

S 小于 n2 ,现在我们需要对于每个 S 算出有几种方案。可以 DP ,考虑从小到大不断插入,现在插入的是当前的最大值,这样比较好处理。f[i][s][j],j 表示有 j 个间隙待插入,S 表示其他规定不再插入的位置对S的贡献和。这样转移时就考虑插入后新多出来的空,是否规定不再插入。还有往边界插的特殊情况需要考虑。

复杂度O(n4)



#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100105;
int n,m,MOD,f[2][10105][115],ans;// f[i][s][j]
LL inv[maxn],fac[maxn],fac_inv[maxn];
LL C(int n,int m){ return fac
*fac_inv[m]%MOD*fac_inv[n-m]%MOD; }
int main(){
scanf("%d%d%d",&n,&m,&MOD); m=m-1+n; // m=X-1+n  C(m-S,n)
fac[0]=1; for(int i=1;i<=100100;i++) fac[i]=fac[i-1]*i%MOD;
inv[1]=1; for(int i=2;i<=100100;i++) inv[i]=(LL)(MOD-MOD/i)*inv[MOD%i]%MOD;
fac_inv[0]=1; for(int i=1;i<=100100;i++) fac_inv[i]=fac_inv[i-1]*inv[i]%MOD;
f[1][0][0]=1;
for(int i=1;i<=n-1;i++){
memset(f[(i&1)^1],0,sizeof(f[(i&1)^1]));
for(int s=0;s<=i*i&&s<=m-n;s++)
for(int j=0;j<=i-1;j++) if(f[i&1][s][j]){
LL f_now=f[i&1][s][j];
if(j){
(f[(i&1)^1][s][j+1]+=f_now*j%MOD)%=MOD;
(f[(i&1)^1][s+(i+1)][j]+=f_now*j*2%MOD)%=MOD;
(f[(i&1)^1][s+2*(i+1)][j-1]+=f_now*j%MOD)%=MOD;
}
(f[(i&1)^1][s][j+1]+=f_now*2%MOD)%=MOD;
(f[(i&1)^1][s+(i+1)][j]+=f_now*2%MOD)%=MOD;
}
}
for(int s=0;s<=n*n&&s<=m-n;s++) (ans+=C(m-s,n)*f[n&1][s][0]%MOD)%=MOD;
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: