bzoj4498: 魔法的碰撞
首先,如果排列确定,那么就可以组合学解决了,不过排列数很多,显然不能枚举。
我们发现如果区间不能重叠的话,总长度减去所有区间长度就是能用的多余格子数。
然而相邻区间可以重叠较小区间一半的长度,因此这些长度就可以作为额外的多余格子!
因此,我们发现,确定排列的目的是确定有多少多余的格子能用。
考虑把魔法师按攻击范围从大到小排序,然后逐个插入
一开始有一个洞,每插一个可以减少一个洞、不变或增加一个洞(洞就表示还能再插)
令dp[i][j][k]表示现在插了i个,有j个洞,目前有k个额外的格子。
然后随便插一插搞一搞
答案即为dp[n][0][i]*C(L+i+n,n)的和
空间时间均为O(n^4+LlogP)
//我觉得我的复杂度应该有问题……TAT
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <algorithm> #define ll long long #define N 43 #define M 1702 #define P 1000000007 using namespace std; int fast_pow(int x,int y){ int ret=1; while (y){ if (y&1) ret=(ll)ret*x%P; x=(ll)x*x%P; y=y>>1; } return ret; } int n,d[N]; int dp[N][N][M]; int L; int fact[1000006+M],inv[1000006+M]; int c(int n,int m){return (ll)fact[n]*inv[m]%P*inv[n-m]%P;} int main(){ scanf("%d%d",&L,&n);--L; for (int i=0;i<n;L-=2*d[i++]) scanf("%d",&d[i]); sort(d,d+n,greater<int>()); memset(dp,0,sizeof(dp)); dp[0][1][0]=1; for (int i=0;i<n;++i) for (int j=1;j<=n-i&&j<=i+1;++j) for (int k=0;k<=(i-j+1)*40;++k)if (dp[i][j][k]){ (dp[i+1][j-1][k+2*d[i]]+=(ll)dp[i][j][k]*j%P)%=P; (dp[i+1][j][k+d[i]]+=(ll)dp[i][j][k]*j*2%P)%=P; (dp[i+1][j+1][k]+=(ll)dp[i][j][k]*j%P)%=P; } for (int i=fact[0]=1;i<=L+(n+2)*40;++i) fact[i]=(ll)fact[i-1]*i%P; for (int i=0;i<=L+(n+2)*40;++i) inv[i]=fast_pow(fact[i],P-2); int ans=0; for (int i=max(-L,0);i<=(n+1)*40;++i) (ans+=(ll)dp[n][0][i]*c(L+i+n,L+i)%P)%=P; printf("%d\n",ans); return 0; }