Loading

[题解] [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 中我们只需要保证每段是否在边界以及相邻两段之间有空位即可,不关心每段的实际位置。

从转移来感受一下上面这句话:

  1. 新建一段 (这一段可以放在边界除外的任意 \(j+1\) 个空隙内);
  2. 合并两段;
  3. 放在其中一段的其中一端;
  4. 新建一段并钦定其为边界;
  5. 接在最左段 (不能为边界) 的左端并钦定为边界,或接在最右段 (不能为边界) 的最右端并钦定为边界;

转移的时候注意判合法。

#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 */
posted @ 2021-09-23 10:01  IrisT  阅读(113)  评论(0编辑  收藏  举报