2018 CCPC网络赛 hdu6444 Neko's loop
题目描述:
Neko has a loop of size n.
The loop has a happy value ai on the i−th(0≤i≤n−1) grid.
Neko likes to jump on the loop.She can start at anywhere. If she stands at i−th grid, she will get ai happy value, and she can spend one unit energy to go to ((i+k)modn)−th grid. If she has already visited this grid, she can get happy value again. Neko can choose jump to next grid if she has energy or end at anywhere.
Neko has m unit energies and she wants to achieve at least s happy value.
How much happy value does she need at least before she jumps so that she can get at least s happy value? Please note that the happy value which neko has is a non-negative number initially, but it can become negative number when jumping
输入:
The first line contains only one integer T(T≤50), which indicates the number of test cases.
For each test case, the first line contains four integers n,s,m,k(1≤n≤104,1≤s≤1018,1≤m≤109,1≤k≤n).
The next line contains n integers, the i−th integer is ai−1(−109≤ai−1≤109)
该题关键在于:一个环,环上每点有一个权值,可以从任意点开始走,问至多走m步情况下所经过点的权值和最大是多少。主要比较难想的在于O(nlogn)地求一个环上至多长为k的字段和的最大值。
主要思路:可以在扫一遍的过程中一个个地求以i为结尾的至多长为k的字段和的最大值。维护一个单调队列,保证队头一直是点{i-k..i-k+1....i-i}的前缀和中的最小值,i的前缀和减去该最小值即为i为结尾的至多长为k的字段和的最大值。
单调队列的维护:1.若队头的时间戳与i距离大于k,则队头出队。2.每次将i的前缀和入队的时候,循环进行操作:队尾若大于i的前缀和则队尾出队。
#include<bits/stdc++.h> #define rep(i,a,b) for(long long i=a;i<=b;++i) using namespace std; long long n,m,s,k; const long long MAXN=1e4+10; long long a[MAXN]; void Input() { scanf("%lld%lld%lld%lld",&n,&s,&m,&k); rep(i,0,n-1) scanf("%lld",a+i); } long long gcd(long long a,long long b) { return b==0?a:gcd(b,a%b); } long long ring[MAXN]; long long head; long long vis[MAXN]; long long pre[MAXN]; void init() { memset(vis,0,sizeof(vis)); } int tail=0; struct Qu { long long t,val; Qu() {} Qu(long long _val,long long _t) { t=_t; val=_val; } }q[2*MAXN]; void update(long long idx) { Qu one(pre[idx],idx+1); while(tail>=head) { if(q[tail].val>=one.val) tail--; else break; } q[++tail]=one; } void debug2(long long i) { printf("i:%lld\n",i); rep(i,head,tail) { printf("val:%lld t:%lld\n",q[i].val,q[i].t); } } long long calc(long long lim,long long len) { // printf("lim:%lld\n",lim); long long presum=0; rep(i,len,2*len-1) ring[i]=ring[i-len]; rep(i,0,2*len-1) { presum+=ring[i]; pre[i]=presum; // printf("%lld ",pre[i]); } head=0; tail=0; long long maxans=0; q[head]=Qu(0,0); rep(i,0,2*len-1) { update(i); int nowlen=(i+1)-q[head].t; if(nowlen>lim) { head++; } // debug2(i); //printf("maxans:%lld val:%lld\n",maxans,pre[i]-q[head].val); maxans=max(maxans,pre[i]-q[head].val); } return maxans; } void debug(long long len) { rep(i,0,len-1) printf("%d ",ring[i]); printf("\n"); } void work(long long icase) { long long num=gcd(n,k); long long ans=0; rep(i,0,n-1) { if(vis[i]) continue; head=0; long long sum=0; long long len=0; for(long long j=i;!vis[j];j=(j+k)%n) { // printf("j:%lld\n",j); ring[head++]=a[j]; sum+=a[j]; len++; vis[j]=1; } long long time=m/len; long long maxseq; if(sum<0) { sum=0; maxseq=calc(min(len - 1, m), len); } else maxseq=calc(m%len,len); // debug(len); long long other=calc(len-1,len); if(m>=len) { if (other > sum + maxseq){ long long res = (time-1) * sum; long long ians = res + other; ans = max(ians,ans); }else{ long long res=time*sum; long long ians=res+maxseq; ans=max(ians,ans); } }else{ long long res=time*sum; long long ians=res+maxseq; ans=max(ians,ans); } } long long finalans=s-ans; if(finalans<0) finalans=0; printf("Case #%lld: %lld\n",icase,finalans); } int main() { long long T; scanf("%lld",&T); rep(tt,1,T) { init(); Input(); work(tt); } return 0; }