是bzoj4498: 魔法的碰撞的哥哥题,我只写了一种
不一样的地方在于贡献有负数,第三维要保存的不能仅仅是0~L,这样空间会炸裂
考虑如何把贡献变成正的
假如要求最优解,那么一定是按顺序排,混乱度为hmax-hmin
反过来想,这启示我们hi-hj,可以用(hi - hi-1)+(hi-1 - hi-2)……(hj+1 - hj)表示出来
那么可以从小到大插入,每次插入给所有段的两端的点的贡献加上hi - hi-1
好妙啊
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int _=1e2; const int maxn=1e2+10; const int maxL=1e3+_; const LL mod=1e9+7; int h[maxn]; LL f[2][maxn][maxL][3]; int main() { // freopen("b.in","r",stdin); // freopen("b.out","w",stdout); int n,L; scanf("%d%d",&n,&L); if(n==1){puts("1");return 0;} for(int i=1;i<=n;i++)scanf("%d",&h[i]); sort(h+1,h+n+1); if(h[n]-h[1]>L){puts("0");return 0;} int now=0; f[now][1][0][0]=1; f[now][1][0][1]=2; for(int i=1;i<n;i++) { memset(f[now^1],0,sizeof(f[now^1])); for(int j=1;j<=i;j++) for(int k=0;k<=L;k++) for(int p=0;p<=2;p++) if(f[now][j][k][p]) { int d=k+(h[i+1]-h[i])*(2*j-p); if(d<=L) { f[now^1][j+1][d][p]=(f[now^1][j+1][d][p]+f[now][j][k][p]*(j-p+1))%mod; f[now^1][j][d][p]=(f[now^1][j][d][p]+f[now][j][k][p]*(2*j-p))%mod; if(j!=1)f[now^1][j-1][d][p]=(f[now^1][j-1][d][p]+f[now][j][k][p]*(j-1))%mod; if(p!=2) { f[now^1][j+1][d][p+1]=(f[now^1][j+1][d][p+1]+f[now][j][k][p]*(2-p))%mod; f[now^1][j][d][p+1]=(f[now^1][j][d][p+1]+f[now][j][k][p]*(2-p))%mod; } } } now^=1; } LL ans=0; for(int i=0;i<=L;i++) ans=(ans+f[now][1][i][2])%mod; printf("%lld\n",ans); return 0; }