题目地址:
http://fzoj.xndxfz.com/JudgeOnline/problem.php?id=2146&csrf=17peCLMJM49glhqlg9KaD7Agf5o5QY87
题目描述:
在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只会移动到i+L到i+R中的一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。开始时,琪露诺在编号0的格子上,只要她下一步的位置编号大于N就算到达对岸。
输入格式:
第 1 行: 3 个正整数 N, L, R
第 2 行: N+1 个整数,第 i 个数表示编号为 i-1 的格子的冰冻指数 A[i-1]
输出格式:
第 1 行:一个整数,表示最大冰冻指数。保证不超过 2^31-1
第 2 行:空格分开的若干个整数,表示琪露诺前进的路线,最后输出-1 表示到达对岸
输入样例:
5 2 3
0 12 3 11 7 -2
输出样例:
11
0 3 -1
数据范围:
对于 60%的数据: N <= 10,000
对于 100%的数据: N <= 200,000
对于所有数据 -1,000 <= A[i] <= 1,000 且 1 <= L <= R <= N
题解:
题目大意我就不多说了,看到这道题可以想到DP,状态转移方程式也不难想出:
f[j]=max(f[k]+a[j],f[j])(j-r<=k<=j-l)
因为需要求一个区间中最大的点,如果选择N的时间扫一遍肯定会爆,所以我们需要使用线段树来维护一个区间中的最大节点的值。大概就这样,有一点需要注意的是数组的范围至少要比N的最大值大6000,不然最后一组数据会爆。下面贴代码吧。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
using namespace std;
const int N=200005;
int tree[N<<2],n,l,r,f[N<<1],a[N<<1];
void pushup(int rt)
{
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
void build(int k,int num,int l,int r,int rt)
{
if(l==r&&l==k)
{
tree[rt]=num;
return ;
}
int mid=(l+r)>>1;
if(k<=mid) build(k,num,ls);
else build(k,num,rs);
pushup(rt);
}
void Init()
{
memset(f,0,sizeof(f));
memset(tree,0xaf,sizeof(tree));
memset(a,0,sizeof(a));
scanf("%d%d%d",&n,&l,&r);
for(int i=0;i<=n;i++)
{
scanf("%d",&a[i]);
}
}
int query(int le,int ri,int l,int r,int rt)
{
if(le<=l&&ri>=r)
{
return tree[rt];
}
int mid=(l+r)>>1;
if(ri<=mid)
{
return query(le,ri,ls);
}
if(le>mid)
{
return query(le,ri,rs);
}
if(le<=mid&&ri>mid)
{
return max(query(le,mid,ls),query(mid+1,ri,rs));
}
}
void dfs(int k)
{
if(k==0)
{
printf("0 ");
return ;
}
for(int i=max(k-r,0);i<=k-l;i++)
{
if(f[i]+a[k]==f[k])
{
int t1=a[i],t2=f[i];
dfs(i);
break;
}
}
if(k>=n)
{
printf("-1");
return ;
}
printf("%d ",k);
return ;
}
void work()
{
f[0]=a[0];
build(0,f[0],0,n+r,1);
for(int i=l;i<=n+r;i++)
{
f[i]=query(max(0,i-r),i-l,0,n+r,1)+a[i];
build(i,f[i],0,n+r,1);
}
int maxn=0,ord;
for(int i=n;i<=n+r;i++)
{
if(f[i]>maxn)
{
maxn=f[i];
ord=i;
}
}
printf("%d\n",maxn);
dfs(ord);
}
int main()
{
Init();
work();
return 0;
}