[bzoj1915] [Usaco2010 Open]奶牛的跳格子游戏

  dp+单调队列优化TAT。。一开始有个条件没细想结果方程推错了TAT。

  主要是转移的时候强制留下哪个点作为回去的落脚点的问题= =。。

  预处理出presum[i]表示1~i个格子中,正数的前缀和(因为在K的小范围内肯定会贪心地选正数的点跳),val[i]表示第i个格子上的数字。

  f[i]表示当前跳到第i个格子,并且留下了一条回去的路的最大总和。

  f[i]=max{ f[j]+presum[i-1]-presum[j] }+val[i-1]+val[i],(i-K<=j<i-1)

    也就是从j跳到i,并且强制(i-1)不走,留作回去的路(但回来的时候一定会走的。。所以先把val[i-1]加上)。

  max{f[i]}并不是最后的答案。。因为对于f[i],我们留下了(i-1)作回去时的第一个点。。所以还可以走完 [i+1,i-1+K]这些点中的正数点 再跳回(i-1)。。。注意边界

 

  一开始太懒。不想算最后多走一段的情况。。所以不是强制i-1不走,而是强制j+1不走。。。结果贪心地取[j+2,i]中的点时可能使j+2走不到。。

  而如果强制j-1不走的话会重复计算。。。所以还是只能强制i-1不走。。。然而这样就会产生走到i后可以多走一段的情况TAT

幸好速度#1弥补了我心灵的创伤(大雾

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const int maxn=250233;
 7 ll f[maxn],presum[maxn],ans,dl[maxn];
 8 int val[maxn],dlpos[maxn];
 9 int i,j,n,m,K,l,r;
10 int ra,fh;char rx;
11 inline int read(){
12     rx=getchar();ra=0;fh=1;
13     while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();
14     if(rx=='-')fh=-1,rx=getchar();
15     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
16 }
17 int main(){
18     n=read();K=read();n++;
19     for(i=2;i<=n;i++)val[i]=read(),presum[i]=presum[i-1]+(ll)(val[i]>0?val[i]:0);//,printf("              %d %lld\n",val[i],presum[i]);
20     f[0]=f[1]=0;
21     l=1;r=0;
22     ll tmp;
23     for(i=2,j=0;i<=n;i++,j++){
24         tmp=f[j]-presum[j];
25         while(l<=r&&dl[r]<=tmp)r--;dl[++r]=tmp;dlpos[r]=j;
26         while(l<r&&dlpos[l]<i-K)l++;
27 //      for(int k=l;k<=r;k++)printf("   %lld %d",dl[k],dlpos[k]);printf("\n");
28         f[i]=dl[l]+presum[i-2]+val[i-1]+val[i];
29 //      printf("%d   %lld\n",i,f[i]);
30     }
31     ans=f[1]+presum[min(1+K,n)];
32     for(i=2;i<=n;i++){
33         f[i]+=(i+K-1<=n)?(presum[i+K-1]-presum[i]):(presum[n]-presum[i]);
34         if(f[i]>ans)ans=f[i];
35     }
36     printf("%lld\n",ans);
37     return 0;
38 }
View Code

 

posted @ 2015-12-28 21:46  czllgzmzl  阅读(506)  评论(0编辑  收藏  举报