square869120Contest #4 E

3 篇文章 0 订阅

题面:

现在有N + 1个火车站排成一行,标号从0~N,现有两种列车:1.普通列车,线路连接了任意车站i和i + 1,可以花费1分钟的时间在相邻车站转移。 2.快速列车,线路包括k个车站,标号分别为{S0,S1,S2,...,Sk-1},可以花费1分钟时间在相邻车站转移,标号满足0 = S1 < S2 < ... < Sk-1 = N。列车长认为只有这两种列车并不方便人们的出行,于是决定新建设一种半快速列车,共包括L个车站{T0,T1,T2,..,TL-1},在相邻车站间的转移还是耗费1分钟,并要求满足以下限制:

1.所有快速列车经过的站台,半快速列车也必须经过

2.在建设完半快速列车的站台后,要求从起点出发能在X分钟内到达任意一个站台

需求出满足限制的快速列车站选址方案

K,X <= 2500,1 <= Si+1 - Si <= 10000,答案对10^9 + 7取模


solution:

按照题目的定义,到达一个车站耗时最少的方案,是先乘坐快速列车,到达该站台左边或右边最近的快速列车站,然后通过半快速列车或者普通列车到达目的地。

再加上半快速列车的选址是基于快速列车的基础上的,那么,对于两个相邻的快速列车站,中间的站台是否作为半快速列车站,显然是相互独立的,所以只需要考虑每一段的情况,最后乘起来就行了

定义一个点的剩余距离,为从起点出发走最短路到达这个点后,还剩下的时间

对于相邻的两个快速列车站Si,Si + 1,设它们的剩余距离分别为ld,rd,一定有ld = rd + 1。此时,枚举离Si最近的半快速列车站台的位置k,一定需要有k - Si <= 2 * ld。由于从rd转移肯定更劣,所以k的剩余距离必定为ld - 1,所以Si ~ k中间的列车站想被覆盖就一定要满足这个条件。

根据上面的性质,能转移过来的k一定是连续一段,所以定义状态f[d][ld][rd]:相邻车站距离为d,左右的剩余距离分别为ld,rd的方案数,就可以利用前缀和差分转移方程了。

但是这样状态是O(SX^2)的,显然不行

第一次我们枚举离左端点最近的半快速列车站的位置,不妨强制令第二次枚举的是离右端点距离最近的半快速列车站的位置,那么就始终有ld == rd || ld == rd + 1,于是状态数就能缩减为O(SX)

注意当中间不设任何半快速列车站就已经能覆盖的情况是多一种方案的,于是简单dp就行,O(SX)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 5005;
const int maxm = 1E4 + 10;
typedef long long LL;
const LL mo = 1000000007;
 
int n,k,x,tail,Ans = 1,s[maxn],f[maxn][maxm];
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}
 
int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	f[0][1] = 1; cin >> n >> k >> x;
	for (int i = 0; i < k; i++) scanf("%d",&s[i]);
	for (int i = 1; i < k; i++) tail = max(tail,s[i] - s[i - 1]);
	for (int i = 1; i <= 2 * x; i++)
	{
		int sum = 0,Max = i + (i & 1);
		for (int j = 1; j <= tail; j++)
		{
			sum = Add(sum,f[i - 1][j - 1]);
			if (j - Max - 1 >= 0) sum = Dec(sum,f[i - 1][j - Max - 1]);
			f[i][j] = sum; if (i + 1 >= j) f[i][j] = Add(f[i][j],1);
		}
	}
	for (int i = 1; i < k; i++) Ans = Mul(Ans,f[2 * x - 2 * i + 1][s[i] - s[i - 1]]);
	cout << Ans << endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值