Neko's loop
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1557 Accepted Submission(s): 362
Problem Description
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.
Input
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)
Output
For each test case, output one line "Case #x: y", where x is the case number (starting from 1) and y is the answer.
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
题意:
有一个n个节点的环,每一个点有一个权值,你到那个点,你就可以获得那个点的权值,同一个点可以获得多次
你选择任意一个起点开始,每一步,你要么选择停止结束,要么走到(i+k)%n这个点
你最多只能走m步,给你一个标准s,问你如果你最后结束的获得的权值要>=s,问你开始之前最少需要
预先得到多少权值?答案是一个非负整数,但走路的时候,你获得的权值可能会变成负的
解析:
线段树+裴蜀定理
遍历数组时,线段树维护的是右端点是i的区间的最大值
maxx:表示1-n权值最大的区间
maxxs:表示1-n内,长度小于等于ss的最大权值的区间
这个题目,走的时候分情况讨论一下就可以了
sum<0时,走的路,一定不会超过一次循环
sum>0时,走到路,要么在m/n次循环中停下,要么在m/n+(m%n?1:0)次循环中停下
比较一下这两种情况,你获得权值哪个打,你就在哪里停下就好了。
这里数字要扩成2倍,因为是环
还有这里因为走的路线是跟gcd(n,k)有关的,如果gcd(n,k)=1,那么就是长度为n的循环,所有点都能被
走到,只有一种情况与起点无关,但是如果gcd(n,k)>1,那么就是长度为n/gcd(n,k)的循环,有gcd(n,k)种情况
与起点有关
那么你对于每一种情况都做一遍就可以了,因为每一种情况不可能有重复的点,所以最后的复杂度还是O(nlogn)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define Max(a,b) (a>=b?a:b)
#define Min(a,b) (a<=b?a:b)
using namespace std;
typedef long long ll;
const int MAXN = 2e4+100;
ll b[MAXN],a[MAXN];
#define lch (root<<1)
#define rch ((root<<1)|1)
typedef struct node
{
//int y;
ll minn;
int pos;
}node;
node Btree[MAXN*5];
ll mark[MAXN*4];
inline void push_up(int root)
{
Btree[root].minn=Max(Btree[lch].minn,Btree[rch].minn);
}
void build(int l,int r,int root) //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{
if(l>r)return;
if(l==r)
{
Btree[root].minn=0;
mark[root]=0;
return ;
}
int mid=(l+r)>>1;
mark[root]=0;
build(l,mid,root<<1);
build(mid+1,r,(root<<1)|1);
push_up(root);
}
inline void pushDown(int root)
{
if(mark[root]!=0)
{
mark[lch]+=mark[root];
mark[rch]+=mark[root];
Btree[lch].minn+=mark[root];
Btree[rch].minn+=mark[root];
mark[root]=0;
}
}
void update(int root,int s1,int e1,int s2,int e2,ll val) //s1,e1表示当前区间,s2,e2表示目标区间
{
if(e1<s2||s1>e2)
return;
if(s1>e1)return;
if(s2<=s1&&e1<=e2)
{
Btree[root].minn+=val;
mark[root]+=val;
return;
}
pushDown(root);
int mid=(s1+e1)>>1;
if(s2<=mid)
update(root<<1,s1,mid,s2,e2,val);
if(mid+1<=e2)
update((root<<1)|1,mid+1,e1,s2,e2,val);
push_up(root);
}
ll query(int root,int s1,int e1,int s2,int e2)
{
if(e1<s2||s1>e2)
return 0;
if(s1>e1)return 0;
if(s1>=s2&&e1<=e2)
{
return Btree[root].minn;
}
pushDown(root);
int mid=(s1+e1)>>1;
ll ansl,ansr;
ansl=ansr=0;
if(s2<=mid)ansl=query(root<<1,s1,mid,s2,e2);
if(mid+1<=e2) ansr=query((root<<1)|1,mid+1,e1,s2,e2);
return Max(ansl,ansr);
}
int m,k;
ll s;
ll solve(int e,int n,int tot)
{
ll sum=0;
for(int i=1;i<=n;i++)
{
a[i]=b[e];
e=(e+k)%tot;
sum+=a[i];
}
for(int i=n+1;i<=2*n;i++)
{
a[i]=a[i-n];
}
build(1,n*2,1);
ll kk=m/n;
ll ss=m%n;
ll maxx,maxxs;
maxx=maxxs=0;
int tmp,tmpx;
int nn=n;
n=n<<1;
for(int i=1;i<=n;i++)
{
tmpx=Max(i-nn+1,1);
update(1,1,n,tmpx,i,a[i]);
maxx=Max(maxx,query(1,1,n,tmpx,i));
if(ss)
{
tmp=Max(i-ss+1,1);
maxxs=Max(maxxs,query(1,1,n,tmp,i));
}
}
if(sum<0)
{
//printf("%lld\n",kk>0?Max(s-maxx,0):Max(s-maxxs,0));
return kk>0?Max(s-maxx,0):Max(s-maxxs,0);
}
else
{
if(kk==0) //printf("%lld\n",Max(s-maxxs,0));
return Max(s-maxxs,0);
else
{
if(maxx>maxxs+sum) //printf("%lld\n",Max(0,s-((kk-1)*sum+maxx)));
return Max(0,s-((kk-1)*sum+maxx));
else //printf("%lld\n",Max(0,s-((kk)*sum+maxxs)));
return Max(0,s-((kk)*sum+maxxs));
}
}
}
int gcd(int a,int b)
{
int ans;
while(b)
{
ans=a%b;
a=b;
b=ans;
}
return a;
}
int main()
{
int n;
int t;
scanf("%d",&t);
int cas=0;
while (t--)
{
cas++;
scanf("%d%lld%d%d",&n,&s,&m,&k);
for(int i=0;i<n;i++) scanf("%lld",&b[i]);
int e=0;
printf("Case #%d: ",cas);
ll ans=s;
int w=gcd(n,k);
if(w!=1)
{
for(int i=0;i<w;i++)
{
ans=Min(ans,solve(e+i,n/w,n));
}
}
else
{
ans=solve(0,n,n);
}
printf("%lld\n",ans);
}
return 0;
}
看了题解发现,可以直接用O(n)的方法求最长子段和...用了之后立即93ms。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define Max(a,b) (a>=b?a:b)
#define Min(a,b) (a<=b?a:b)
#define inf (0x3f3f3f3f3f3f3f3f)
using namespace std;
typedef long long ll;
const int MAXN = 2e4+100;
ll b[MAXN],a[MAXN];
ll Q[MAXN];
ll qsum[MAXN];
#define lch (root<<1)
#define rch ((root<<1)|1)
typedef struct node
{
//int y;
ll minn;
int pos;
}node;
ll solve(ll num[], ll sum[], int len, int k, ll &ansl, ll &ansr) {//最大子段和模板
ll ans = -inf;
int front = 0, rear = 0;
for (int i = 1; i <= len+k; i++) {
while (front < rear && sum[i-1] < sum[Q[rear-1]]) rear--;
Q[rear++] = i-1;
while (front < rear && i-Q[front] > k) front++;
if (sum[i] - sum[Q[front]] > ans) {
ans = sum[i] - sum[Q[front]];
ansl = Q[front]+1;
ansr = i;
}
}
return ans;
}
int m,k;
ll s;
ll solve(int e,int n,int tot)
{
ll sum=0;
qsum[0]=0;
for(int i=1;i<=n;i++)
{
a[i]=b[e];
e=(e+k)%tot;
sum+=a[i];
qsum[i]=qsum[i-1]+a[i];
}
for(int i=n+1;i<=2*n;i++)
{
a[i]=a[i-n];
qsum[i]=qsum[i-1]+a[i];
}
ll kk=m/n;
ll ss=m%n;
ll maxx,maxxs;
maxx=maxxs=0;
int tmp,tmpx;
int nn=n;
ll tmp1,tmp2;
maxx=solve(a,qsum,n,n,tmp1,tmp2);
maxxs=solve(a,qsum,n,ss,tmp1,tmp2);
if(sum<0)
{
//printf("%lld\n",kk>0?Max(s-maxx,0):Max(s-maxxs,0));
return kk>0?Max(s-maxx,0):Max(s-maxxs,0);
}
else
{
if(kk==0) //printf("%lld\n",Max(s-maxxs,0));
return Max(s-maxxs,0);
else
{
if(maxx>maxxs+sum) //printf("%lld\n",Max(0,s-((kk-1)*sum+maxx)));
return Max(0,s-((kk-1)*sum+maxx));
else //printf("%lld\n",Max(0,s-((kk)*sum+maxxs)));
return Max(0,s-((kk)*sum+maxxs));
}
}
}
int gcd(int a,int b)
{
int ans;
while(b)
{
ans=a%b;
a=b;
b=ans;
}
return a;
}
int main()
{
int n;
int t;
scanf("%d",&t);
int cas=0;
while (t--)
{
cas++;
scanf("%d%lld%d%d",&n,&s,&m,&k);
for(int i=0;i<n;i++) scanf("%lld",&b[i]);
int e=0;
printf("Case #%d: ",cas);
ll ans=s;
int w=gcd(n,k);
if(w!=1)
{
for(int i=0;i<w;i++)
{
ans=Min(ans,solve(e+i,n/w,n));
}
}
else
{
ans=solve(0,n,n);
}
printf("%lld\n",ans);
}
return 0;
}