题目链接:点击这里
解题思路:
根据k我们会得到一个循环周期,将在同一个循环节的点都归为同一类,因为归为同一类的点走过一个循环周期得到的快乐值时一样的。
很明显如果循环周期是负的那一定就不走一个循环了。那直接求最长不超过m的最大连续子段和就行了。
如果循环周期大于0,就有两种可能了(循环周期次数a=m/len(len为周期长度),b=m%n):
1.走完a圈后再加上不超过b的最大连续字段和
2.走完a-1圈后再加上不超过len的最大连续字段和,因为刚好走完一圈不一定会比走了一部分多,加上一圈反而会使最大值变少。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define lowbit(x) x&(-x)
const int mod = 1e9+7;
const int mx = 1e5+5;
typedef long long ll;
ll n,m,k,s;
bool vis[mx];
vector<ll>g[mx];
ll sum[mx];
ll a[mx];
ll q[mx];
ll l,r;
void del(ll k,ll m){
while(l<r&&k-q[l]>m)l++;
}
void ins(ll k){
while(l<r&&sum[q[r-1]]>=sum[k]) r--;
q[r++] = k;
}
ll getans(ll i,ll k){
int n = g[i].size();
for(int j = 1; j <= n; j++)
sum[j] = g[i][j-1],sum[j] += sum[j-1];
for(int i = 1; i <= n; i++)
sum[i+n] = sum[n]+sum[i];
for(int i = 1; i <= n; i++)
sum[i+2*n] = sum[2*n]+sum[i];
l = r = 0;
ll ans = 0;
ins(0);
for(int i = 1; i <= 3*n; i++){
del(i,k);
if(ans<sum[i]-sum[q[l]])
ans = sum[i]-sum[q[l]];
ins(i);
}
return ans;
}
int main(){
int t,ca = 1;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld%lld",&n,&s,&m,&k);
for(int i = 0; i < n; i++)
g[i].clear();
for(int i = 0; i < n; i++)
scanf("%lld",&a[i]);
int cnt = 0;
memset(vis,0,sizeof(vis));
for(int i = 0; i < n; i++){
if(!vis[i]){
int j = i;
while(!vis[j]){
vis[j] = 1;
g[cnt].push_back(a[j]);
j = (j+k)%n;
}
cnt++;
}
}
ll ans = 0;
for(int i = 0; i < cnt; i++){
int len = g[i].size();
ll tmp = 0;
for(int j = 0; j < len; j++)
tmp += g[i][j];
ans = max(ans,getans(i,m));
if(tmp<0)
continue;
ll op = m%len;
ll d = m/len;
tmp = max(d-1,0ll)*tmp;
if(d>=1)
op += len;//把最后一圈加进去考虑走完一圈还是不走那个大
if(op==0){
ans = max(ans,tmp);
continue;
}
ans = max(ans,tmp+getans(i,op));
}
printf("Case #%d: %lld\n",ca++,max(0ll,s-ans));
}
return 0;
}