HDU 6444 Neko's loop(单调队列)

Description

给出 n n n个数 a 0 , . . . , a n − 1 a_0,...,a_{n-1} a0,...,an1顺序围成一圈,任选起点,每次可以从 a i a_i ai跳到 a ( i + k ) % n a_{(i+k)\%n} a(i+k)%n位置,至多跳 m m m次,问所经过的位置的值之和至少需要加多少才能达到 s s s,如果该值已经不小于 s s s则输出 0 0 0

Input

第一行一整数 T T T表示用例组数,每组用例首先输入四个整数 n , s , m , k n,s,m,k n,s,m,k,之后输入 n n n个整数 a i a_i ai

( T ≤ 50 , 1 ≤ n ≤ 1 0 4 , 1 ≤ s ≤ 1 0 18 , 1 ≤ m ≤ 1 0 9 , 1 ≤ k ≤ n , − 1 0 9 ≤ a i ≤ 1 0 9 ) (T\le 50,1\le n\le 10^4,1\le s\le 10^{18},1\le m\le 10^9,1\le k\le n,-10^9\le a_i\le 10^9) (T50,1n104,1s1018,1m109,1kn,109ai109)

Output

输出答案

Sample Input

2
3 10 5 2
3 2 1
5 20 6 3
2 3 2 1 5

Sample Output

Case #1: 0
Case #2: 2

Solution

i i i位置出发,每次跳 k k k步所能经过的位置编号必然为 a ( i + t ⋅ g c d ( k , n ) ) % n a_{(i+t\cdot gcd(k,n))\%n} a(i+tgcd(k,n))%n n g c d ( k , n ) \frac{n}{gcd(k,n)} gcd(k,n)n个位置,也就是说本质不同的所经位置序列只有 g c d ( k , n ) gcd(k,n) gcd(k,n)个,对于每个序列单独考虑,问题转化为从一个序列选择一个位置开始至多走 m m m步所能得到的最大和,分两种情况:

1.若该序列和 s u m < 0 sum<0 sum<0,那么不会一直遍历这个序列,至多只会走 m i n ( m , l e n ) min(m,len) min(m,len)步,其中 l e n = n g c d ( k , n ) len=\frac{n}{gcd(k,n)} len=gcd(k,n)n为序列长度

2.若该序列和 s u m ≥ 0 sum\ge 0 sum0,那么前期会不断重复遍历这个序列,但是后面若干步需要单独考虑,要么是循环走 ⌊ m l e n ⌋ \lfloor\frac{m}{len}\rfloor lenm次,最后至多走 m % l e n m\% len m%len步,要么是循环走 ⌊ m l e n ⌋ − 1 \lfloor\frac{m}{len}\rfloor-1 lenm1次,最后至多走 l e n len len步(需要考虑这种情况是因为对于最后 l e n + m % l e n len+m\% len len+m%len步,不一定是走一个循环再走若干步最优,例如 1   1   − 1   − 1   1 1\ 1\ -1\ -1\ 1 1 1 1 1 1走六步,如果只取前两个 1 1 1和最后一个 1 1 1所得答案是 3 3 3,但是如果走一个循环拿到 1 1 1再走一步拿一个 1 1 1的话答案只有 2 2 2

故剩余问题为给出一个序列,求该序列的长度不超过 x x x的最大循环子段和,由于 x x x不会超过序列长度,故把序列复制之后可以把环上问题变成链上问题,求出该序列前缀和后,用单调队列维护递增序列,那么当前值插入之后,队首元素即为前面经过值中的最小值,两者做差维护答案即可

Code

#include
#include
using namespace std;
typedef long long ll;
const int maxn=20005;
int a[maxn],que[maxn];
ll sum[maxn];
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
ll Solve(int n,int m)
{
	ll ans=0;
	int st=1,ed=0;
	for(int i=1;i<=n;i++)
	{
		while(st<=ed&&i-que[st]>m)st++;
		while(st<=ed&&sum[que[ed]]>sum[i])ed--;
		if(st<=ed)ans=max(ans,sum[i]-sum[que[st]]);
		que[++ed]=i;
	}
	return ans;
}
int main()
{
	int T,n,m,k,Case=1;
	ll s;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%lld%d%d",&n,&s,&m,&k);
		for(int i=0;i=len)
				{
					res=sum[len]*(m/len-1)+Solve(2*len,len);
					ans=max(ans,res);
				}
			}
		}
		printf("Case #%d: %lld\n",Case++,max(0ll,s-ans));
	}
	return 0;
}

你可能感兴趣的:(HDU,单调栈/单调队列)