[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 }