http://acm.hdu.edu.cn/showproblem.php?pid=6444
单调队列求有限制最大子段https://blog.csdn.net/sinat_34550050/article/details/52274141
先找出所有循环节 对于每个循环节 如果总权值为正 如果没有步数限制 那肯定是跑越多圈越好 但是最多m步 策略就需要调整了
一开始想的假设步数为m 循环节长度为len 那就绕m/len圈 再找一个m%len的最大子段 但并没有这么简单
比如m=23 len=11 循环节为1 1 1 1 1 1 -1 -1 -1 -1 -1 按这种策略就只有3 其实是7
所以最后一圈是不能想当然地走掉的 而要灵活处理 应该先绕(m-len)/len圈 再求m%len+len的最大子段
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const ll N=0x3f3f3f3f3f3f3f3f;
ll ary[10010],tmp[30010];
ll s;
int book[10010],que[30010];
int n,m,k,len;
ll getmax(ll a,ll b)
{
if(a>b) return a;
else return b;
}
ll getmin(ll a,ll b)
{
if(a<b) return a;
else return b;
}
ll solveII(int lim)
{
ll res;
int i,head,tail;
head=0,tail=0,res=0;
//printf("***%d %d***\n",len,lim);
//for(i=1;i<=3*len;i++) printf("*%lld*\n",tmp[i]);
for(i=1;i<=3*len;i++)
{
while(head<tail&&tmp[i-1]<tmp[que[tail-1]]) tail--;
que[tail++]=i-1;
while(head<tail&&i-que[head]>lim) head++;
//printf("%lld %lld %lld*\n",res,tmp[i],tmp[que[head]]);
res=getmax(res,tmp[i]-tmp[que[head]]);
}
return res;
}
ll solveI()
{
ll sum,gou;
int i;
sum=0;
for(i=1;i<=len;i++) sum+=tmp[i];
for(i=len+1;i<=3*len;i++) tmp[i]=tmp[i-len];
for(i=1;i<=3*len;i++) tmp[i]+=tmp[i-1];
if(sum>0)
{
if(m>len)
{
gou=((m-len)/len);
return gou*sum+solveII(m%len+len);
}
else return solveII(m);
}
else return solveII(getmin(m,len));
}
int main()
{
ll ans;
int t,cas,i,j;
scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
scanf("%d%lld%d%d",&n,&s,&m,&k);
for(i=0;i<n;i++) scanf("%lld",&ary[i]);
memset(book,0,sizeof(book));
memset(tmp,0,sizeof(tmp));
ans=N;
for(i=0;i<n;i++)
{
j=i,len=0;
if(book[j]==0)
{
while(book[j]==0)
{
tmp[++len]=ary[j];
book[j]=1;
j=(j+k)%n;
}
ans=getmin(ans,getmax(0ll,s-solveI()));
}
}
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}
/*
2
5 1000 5 1
1 1 -1 -1 1
5 1000 6 1
1 1 -1 -1 1
1
5 1000 5 1
1 1 -1 -1 1
1
5 1000 6 1
1 1 -1 -1 1
*/