1. 程式人生 > >[Codeforces 721E]Road to Home

[Codeforces 721E]Road to Home

nan 滿足 highlight ctype light blog body 隊列 type

題目大意:有一條長為l的公路(可看為數軸),n盞路燈,每盞路燈有照射區間且互不重疊,有個人要走過這條公路,他只敢在路燈照射的地方唱歌,固定走p唱完一首歌,歌曲必須連續唱否則就要至少走t才能繼續唱。問你最多能唱幾首歌?

解題思路:一道dp的題目。

首先有一個結論:對於一段區間,你能唱幾首歌就唱幾首歌。

因為如果你少唱一首歌,在下個區間最多也就多唱一首歌,與你在前面唱這首歌是一樣的。

所以我們設f[i]表示前i個區間最多唱的歌的數量,g[i]表示在f[i]的前提下,最早的停止唱歌的位置。

很顯然有$f[i]=max \{ f[j]+\lfloor\frac{(r[i]-max(g[j]+t,l[i]))}{p}\rfloor \}$,$g[i]=min\{ r[i]-(r[i]-max(g[j]+t,l[i]))\mod p \}$ ($j<i$)。

這樣的話時間復雜度就是$O(n^2)$,難以接受。

註意到f和g都是滿足單調性的(f很顯然,g的話,總不可能唱第2首歌比唱第1首歌還前面),所以容易想到單調隊列。

這樣時間復雜度就優化到$O(n)$。

具體單調隊列的操作詳見代碼。

C++ Code:

#include<cstdio>
#include<cstring>
#include<cctype>
struct Struct{
	int f,g;
}q[100005];
int l,n,p,t,head,tail;
inline int max(int a,int b){return a>b?a:b;}
inline int readint(){
	char c=getchar();
	for(;!isdigit(c);c=getchar());
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^‘0‘);
	return d;
}
int main(){
	l=readint(),n=readint(),p=readint(),t=readint();
	q[0]=(Struct){0,-0x3f3f3f3f};
	head=0,tail=0;
	int ans=0;
	while(n--){
		int x=readint(),y=readint();
		int nans=0,ng=0;
		head?--head:1;
		while(head<=tail){
			if(q[head].g+p+t>y)break;
			int ff=q[head].f+(y-max(q[head].g+t,x))/p,
			gg=y-(y-max(q[head].g+t,x))%p;
			if(ff>nans||ff==nans&&gg<ng)nans=ff,ng=gg;
			++head;
		}
		if(ans<nans){
			ans=nans;
			q[++tail]=(Struct){nans,ng};
		}
	}
	return!printf("%d\n",ans);
}

[Codeforces 721E]Road to Home