飞扬的小鸟II
时间限制: 1 Sec 内存限制: 128 MB提交: 59 解决: 8
[ 提交][ 状态][ 讨论版]
题目描述
有n棵大树从左到右排成一排,编号为1到n,每棵有高度hi与疲劳值wi。 有一只鸟儿现在站在最左侧的1号大树上,它想飞到第n棵树上去,但它不 能连续飞行太远,当它在第i棵树上时,只能飞到第i + 1, i + 2,..., i + k棵 树上,并获得对应大树的疲劳值;同时,假如它飞到的那棵树的高度不小 于当前这棵树的高度,那么它会获得额外的d疲劳值。求飞到第n棵树的最 小疲劳值是多少。
输入
第一行三个整数n、k和d,由空格分割,表示树有多少棵,飞行的最大距离,以及额外的疲劳值。 接下来n行,第i行包含两个整数hi和wi,表示第i棵树的高度和疲劳值。
输出
输出一行,表示最小疲劳值。
样例输入
5 2 1
4 3
3 2
5 3
2 1
6 1
样例输出
8
提示
最优策略是1 → 2 → 4 → 5,总疲劳值是3 + 2 + 1 + 1,并在4 → 5时支付额外的1疲劳值。
对于20%的数据,n ≤ 1000;
对于另外40%的数据,d = 0;
对于100%的数据,1 ≤ k < n ≤ 3 ∗ 105, 0 ≤ hi ≤ 109, 0 ≤ wi ≤ 103, d ∈{0, 1}。
很容易想到dp,更容易得到状态转移dp[i]=min(dp[i],dp[q[j]]+(p[q[j]].h<=p[i].h?d:0)+p[i].w)。
然后,怎么去维护dp,如果跑区间的话n*k肯定超时,容易发现dp是一个递增序列,然后状态转移是dp[i]=min(dp[j])+cost,很容易得到前k个数去用单调队列优化。
剩下的就是优化一下队列了。
代码实现:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=1e6+5;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll Fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n;n=n*n;}return r;}
ll upd(ll x,ll v){x=x+v>=mod?x+v-mod:x+v;return x;}
struct node{
int h,w;
}p[maxn];
int dp[maxn],q[maxn];
int main()
{
int n,k,d,i,j;
scanf("%d%d%d",&n,&k,&d);
for(i=1;i<=n;i++)
{
scanf("%d%d",&p[i].h,&p[i].w);
}
dp[1]=p[1].w;
int tail=1,head=0;q[0]=1;
for(i=2;i<=n;i++)
{
dp[i]=inf;
// cout<<"First: "<<head<<" "<<tail<<endl;
while(i-q[head]>k)
head++;
for(j=head;j<tail&&dp[q[j]]==dp[q[head]];j++)
{
dp[i]=min(dp[i],dp[q[j]]+(p[q[j]].h<=p[i].h?d:0)+p[i].w);
}
while(dp[i]<dp[q[tail-1]]&&head<tail)
tail--;
q[tail++]=i;
// cout<<"Second: "<<head<<" "<<tail<<endl;
}
printf("%lld\n",dp[n]);
return 0;
}