zoukankan      html  css  js  c++  java
  • hdu6444 2018中国大学生程序设计竞赛

    Neko's loop
    Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
    Total Submission(s): 1386 Accepted Submission(s): 316
    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

    思路

    • 首先通过观察可以发现从i开始走,每次走到(i+k)%n,总可以走回i,存在循环,因此暴力把每个循环处理出来,每处理出来一个循环就计算一个
      大佬们都用裴蜀定理定理直接知道了不同循环的数量gcd(n,k),还有每个循环的长度n/gcd(n,k)...orz,看了半天并不知道为什么,求教呀
    • 然后就是对每个循环串求最长子段和
      • 首先将前缀和处理出来,线段树维护区间最小值(前缀和),每次求i-m~i-1前缀和的最小值,然后用sum[i]-sum[min]更新答案
      • 依旧将前缀和处理出来,用递增的单调队列,维护前面i-m~i-1之间前缀和的最小值
    • 关于对于循环串的处理

      假设循环串的长度为len,能走m步

      • 考虑m/len(默认作为横跨两段的串)和m%len两段,我们只需要在一个长度为2*len的循环串中,找到长度不超过m%len的最长连续子段即可,然后看看一整串的和是否大于零,若大于0,则加上m/len个sum[len](整串的贡献)即可
      • 若m>len,则有另外一种可能,就是m%len+m/len个整段这种分配方式并不是最优的,原因在于

      假设我们单独取m%len(横跨串)和一整段len来看,可能会存在一个长度大于m%len的横跨串的贡献比m%len+len还大,因此我们可以舍弃一段len,然后使得横跨串的长度从m%len变成len


    #include<bits/stdc++.h>
    #define ll long long
    #define M 10005
    using namespace std;
    int T,ks,n,tot,m,vi[M],tg[M],len,i,j,k,h,t,Q[M],tp;
    ll a[M],s[M<<1],S,ans,re;
    int main(){
    	scanf("%d",&T);
    	for(ks=1;ks<=T;ks++){
    		re=-1e16;
    		memset(vi,0,sizeof(vi));
    		scanf("%d%lld%d%d",&n,&S,&m,&k);
    		for(i=0;i<n;i++)scanf("%lld",&a[i]);
    	    for(i=0;i<n;i++){
    	    	len=0;
    	        for(j=i;;j=(j+k)%n){
    	    	   if(!vi[j]){
    	    	   	vi[j]=1;
    	    	   tg[++len]=j;	
    	    	   }
    	    	   else break;
    	        }
    	        if(len==0)continue;
    	        for(j=1;j<=2*len;j++){
    	    		if(j<=len)s[j]=s[j-1]+a[tg[j]];
    	    		else s[j]=s[j-1]+a[tg[j-len]];
    	    	}
    	    	ans=0;
    	    	h=t=0;Q[t++]=0;tp=m;
    	    	if(s[len]>=0){ans+=s[len]*(tp/len);tp%=len;}
    	    	else tp=min(tp,len);
    	    	for(j=1;j<=2*len;j++){
    	    		while(h<t&&j-tp>Q[h])h++;
    	    		if(h<t)re=max(re,ans+s[j]-s[Q[h]]);
    	    		while(h<t&&s[Q[t-1]]>=s[j])t--;
    	    		Q[t++]=j;
    	    	}
    	    	if(m>=len){
    	    		tp=m-len;ans=0;
    	    		if(s[len]>=0)ans+=s[len]*(tp/len);
    	    		tp=len;
    	    		h=t=0;Q[t++]=0;
    	    		for(j=1;j<=2*len;j++){
    	    			while(h<t&&j-tp>Q[h])h++;
    	    		    if(h<t)re=max(re,ans+s[j]-s[Q[h]]);
    	    		    while(h<t&&s[Q[t-1]]>=s[j])t--;
    	    		    Q[t++]=j;
    	    		}
    	    	}
    	    }
    	    printf("Case #%d: %lld
    ",ks,max(0ll,S-re));
    	}
    }
    
  • 相关阅读:
    web--ajax--json
    4.26
    4.25
    4.23
    4.22
    4.20
    4.19
    4.18
    4月问题总结章
    4.17
  • 原文地址:https://www.cnblogs.com/VIrtu0s0/p/9577926.html
Copyright © 2011-2022 走看看