数据范围说明正解是 O ( n ) O(n) O(n)的
自己考虑应该会发现,玩家拿走的石子是连续的一段
假设不连续,比如取了区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]和区间 [ l 2 , r 2 ] [l_2,r_2] [l2,r2]
显然我可以取 [ l 3 , r 2 ] [l_3,r_2] [l3,r2]的糖果,有 l 3 < = l 1 l_3<=l_1 l3<=l1且 [ l 3 , l 2 − 1 ] [l_3,l_2-1] [l3,l2−1]的糖果大于等于 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]
感性考虑,就是前面过早带着的糖果会变成累赘
有了这个启发,我们就很容易想到尺取法
枚举右端点,左端点不断往右逼近, s u m sum sum为当前花费事件
右端点每次往右挪一位到 r r r,增加了 [ l , r − 1 ] [l,r-1] [l,r−1]的所有糖果一个花费
所以 s u m + = p r e [ r − 1 ] − p r e [ l − 1 ] sum+=pre[r-1]-pre[l-1] sum+=pre[r−1]−pre[l−1]
左端点每次往右挪一位到 l l l
s u m − = a [ l ] ∗ ( r − l ) sum -= a[l]*(r-l) sum−=a[l]∗(r−l)
然后 [ l , r ] [l,r] [l,r]是能完全拿走的价值, a [ l − 1 ] a[l-1] a[l−1]也可以拿走部分价值,判断一下即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 6e7+10;
int n,t,type,a[maxn],p,pre[maxn];
signed main()
{
cin >> n >> t >> type;
if( type==1 )
{
for(int i=1;i<=n;i++) scanf("%lld",&a[i] );
}
else
{
scanf("%lld%lld",&a[1],&p);
for(int i=2;i<=n;i++)
{
int x = a[i-1]^(a[i-1]<<13);
int y = x^(x>>17);
a[i] = (y^(y<<5))%p+1;
}
}
for(int i=1;i<=n;i++) pre[i] = pre[i-1]+a[i];
int sum = 0,ans = 0;
for(int r=1,l=1;r<=n;r++)
{
sum += pre[r-1]-pre[l-1];//拿着[l,r]的宝石,只有[l,r-1]需要计算贡献
while( sum>t&&l<r )
{
sum -= a[l]*(r-l);
l++;
}
if( l==1 ) ans = max( ans,pre[r]-pre[l-1] );
else
ans = max( ans,pre[r]-pre[l-1]+(t-sum)/(r-l+1) );
}
cout << ans;
}