解法
首先,跟区间和和滑动窗口有关系
计算区间和,首先要把前缀和算出来
总之,要先固定一个区间的端点,可以是左,也可以是右。假如我固定的是左端点l
,要找合适的右端点r
。
要保证这个区间奇数个数不超过o
个
然后需要找到r
,使得P[r]-P[l-1]
是满足P[r]-P[l-1]<=d
且最大的那个,也就是说,P[r]
要是不超过P[l-1]+d
但最接近它的一个
这个得用upper_bound
来做(如果固定的是右端点就是lower_bound
)
在C++里用multiset
可以做(因为窗口里可能有相同的值!),删除的时候记得相同的值只能删除一个!#include <stdio.h>
这里的滑动窗口可以用双指针实现,当l
增加1的时候,r
只可能增加不可能减少,所以就是类似于尺取法。
#include <stdio.h>
#include <string>
#include <iostream>
#include <memory.h>
#include <stdlib.h>
#include <set>
#include <cmath>
#include <vector>
#include <algorithm>
#define MAXN 500010
#define NINF 0x8000000000000000
#define INF 1000000010
using namespace std;
typedef long long lld;
lld A[MAXN],P[MAXN];
multiset<lld> minV;
void solve(lld n,lld o, lld d) {
P[0]=0;
for(int i=1;i<=n;++i)
P[i] = P[i-1]+A[i];
minV.clear();
lld ans = NINF;
lld odds = 0;
int l=1,r=l;
for(;l<=n;++l) {
r = max(r,l);
while(r<=n) {
if(A[r]%2==0) minV.insert(P[r]);
else {
if(odds<o) {
minV.insert(P[r]);
odds++;
} else break;
}
r++;
}
auto it = minV.upper_bound(d+P[l-1]);
if(it!=minV.begin()) {
it--;
ans = max(*it-P[l-1],ans);
}
if(l<r) {
if(A[l]%2!=0) odds--;
minV.erase(minV.find(P[l]));
}
}
if(ans==NINF) printf("IMPOSSIBLE\n");
else printf("%lld\n",ans);
}
int main() {
// freopen("obj.txt","r",stdin);
// freopen("my.out","w",stdout);
int t;
scanf("%d",&t);
for(auto round=1;round<=t;++round) {
lld n,o,d,x1,x2,a,b,c,m,l;
scanf("%lld%lld%lld",&n,&o,&d);
scanf("%lld%lld%lld%lld%lld%lld%lld",&x1,&x2,&a,&b,&c,&m,&l);
A[1] = x1+l;
A[2] = x2+l;
for(int i=3;i<=n;++i) {
lld tmp = x2;
x2 = (a*x2%m+b*x1%m+c)%m;
x1 = tmp;
A[i] = x2+l;
}
printf("Case #%d: ",round);
solve(n,o,d);
}
}