[题解] [LOJ2743]「JOI Open 2016」摩天大楼
题目大意
将 \(N\) 个互不相同的整数 \(A_1 , A_2 , ⋯ , A_N\) 任意排列成 \(B_1 , B_2 , ⋯ , B_N\) 。
要求 \(∑^{N−1}_{i=1} |B_{i+1} − B_i | ≤ L\)
计数方案数 \(N ≤ 100\ L ≤ 1000\)。
解题思路
是一个比较经典的 DP 方式。但是大家都不屑于讲清楚这个转移,所以只好从网上找一篇记录一下。
首先考虑将贡献差分一下,假设在 \(a_i\) 放下去前一刻有 \(i\) 个空位,则进行放置这个操作的贡献就是 \((a_i-a_{i-1})\times i\)。
那么考虑从小到大放置,每一时刻就会是一些连续段。
设 \(f[i][j][s][d]\) 表示已经放置前 \(i\) 个数,分成 \(j\) 段,目前贡献为 \(s\),有 \(d\) 个边界已确定的方案数。
注意这个 DP 中我们只需要保证每段是否在边界以及相邻两段之间有空位即可,不关心每段的实际位置。
从转移来感受一下上面这句话:
- 新建一段 (这一段可以放在边界除外的任意 \(j+1\) 个空隙内);
- 合并两段;
- 放在其中一段的其中一端;
- 新建一段并钦定其为边界;
- 接在最左段 (不能为边界) 的左端并钦定为边界,或接在最右段 (不能为边界) 的最右端并钦定为边界;
转移的时候注意判合法。
#include <set>
#include <map>
#include <queue>
#include <bitset>
#include <vector>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N(105), M(1005), mod(1e9 + 7);
int n, L, a[N];
int b[N];
int f[N][N][M][3];
inline void read(int &x){
x = 0; int f = 1, c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)) x = x * 10 + c - 48, c = getchar();
x *= f;
}
inline void MOD(int &x){ x = x + ((x >> 31) & mod); }
int main(){
read(n), read(L);
for(int i(1); i <= n; ++i) read(a[i]);
if(n == 1) return puts("1"), 0;
sort(a + 1, a + n + 1);
for(int i(2); i <= n; ++i) b[i] = a[i] - a[i - 1];
f[0][0][0][0] = 1;
for(int i(0); i < n; ++i)
for(int j(0); j <= i; ++j)
for(int s(0); s <= L; ++s)
for(int d(0); d <= 2; ++d){
if(!f[i][j][s][d]) continue;
int more = b[i + 1] * (j * 2 - d); if(s + more > L) continue;
MOD(f[i + 1][j + 1][s + more][d] += 1LL * f[i][j][s][d] * (j + 1 - d) % mod - mod);
if(j) MOD(f[i + 1][j - 1][s + more][d] += 1LL * f[i][j][s][d] * (j - 1) % mod - mod);
MOD(f[i + 1][j][s + more][d] += 1LL * f[i][j][s][d] * (2 * j - d) % mod - mod);
if(d == 2) continue;
MOD(f[i + 1][j + 1][s + more][d + 1] += 1LL * f[i][j][s][d] * (2 - d) % mod - mod);
if(j) MOD(f[i + 1][j][s + more][d + 1] += 1LL * f[i][j][s][d] * (2 - d) % mod - mod);
}
int ans = 0;
for(int i(0); i <= L; ++i) MOD(ans += f[n][1][i][2] - mod);
printf("%d\n", ans);
return 0;
}
/* Hemerocallis */