传送门

数据范围说明正解是 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,l21]的糖果大于等于 [ 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,r1]的所有糖果一个花费

所以 s u m + = p r e [ r − 1 ] − p r e [ l − 1 ] sum+=pre[r-1]-pre[l-1] sum+=pre[r1]pre[l1]

左端点每次往右挪一位到 l l l

s u m − = a [ l ] ∗ ( r − l ) sum -= a[l]*(r-l) sum=a[l](rl)

然后 [ l , r ] [l,r] [l,r]是能完全拿走的价值, a [ l − 1 ] a[l-1] a[l1]也可以拿走部分价值,判断一下即可

#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; 
}