UVaLive6435(dp)

要点

  • 题意:两个数据传输数列,每个数列里有若干个数据包并给出发出时间(t),每个数据包到达的时间(T)(t <= T < t + D),问有多少种到达序列。
  • 将题目转化为第二个数列插入第一个数列。用第一个数列将时间点划分为(N+1)个部分,对于第二数列的每个数据包,预处理它左右范围,即这(N+1)个空,它可以插入哪些空。
  • (i)为第二个数列的第(i)个,(j)为第一个数列的(j-1)(j)的中间空,(dp[i][j])表示把(i)插到(j)空里的方案数,转移即是保证(i-1)(i)的前面某一空即可。
#include <cstdio>
#include <vector>
using std::vector;

typedef long long ll;
const int maxn = 5e4 + 5;
const int mod = 1e9 + 9;
int T, N, M, D;
int a[maxn], b[maxn];
ll dp[maxn][205];
vector<int> v[maxn];

int main() {
	scanf("%d", &T);
	for (int kase = 1; kase <= T; kase++) {
		scanf("%d %d %d", &N, &M, &D);
		for (int i = 1; i <= N; i++)
			scanf("%d", &a[i]);
		for (int i = 1; i <= M; i++)
			scanf("%d", &b[i]);

		for (int p = 1, i = 1; i <= M; i++) {
			v[i].clear();
			for (; p <= N && a[p] + D <= b[i]; p++);
			for (; p <= N && b[i] + D > a[p]; p++)
				v[i].push_back(p);
			v[i].push_back(p), p = v[i][0];//p:1~n+1
		}

		for (int j = 0; j < v[1].size(); j++)
			dp[1][j] = 1;
		for (int i = 2; i <= M; i++) {
			int p = 0;
			ll sum = 0LL;
			for (int j = 0; j < v[i].size(); j++) {
				for (; p < v[i - 1].size() && v[i - 1][p] <= v[i][j]; p++)
					sum = (sum + dp[i - 1][p]) % mod;
				dp[i][j] = sum;
			}
		}

		ll ans = 0LL;
		for (int j = 0; j < v[M].size(); j++)
			ans = (ans + dp[M][j]) % mod;
		printf("Case #%d: %lld
", kase, ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/AlphaWA/p/10809172.html