HDU-5662 YJQQQAQ and the function (枚举&&单调栈)

来源:互联网 发布:淘宝网红店铺有哪些 编辑:程序博客网 时间:2024/06/05 22:30

YJQQQAQ and the function

http://acm.hdu.edu.cn/showproblem.php?pid=5662

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description
YJQQQAQ has an array A of length n. He defines a function fl,r,k where l,r,k are positive integers that satisfies lr and r×kn, and the value of the function equals to p×q×k where p equals to the sum value of Al×k,A(l+1)×k,...,Ar×k and q equals to the minimal value of them. YJQQQAQ wants to choose the positive integers l,r,k carefully to maximize the value of the function.
 

Input
The first line contains an integer T(1T3)——The number of the test cases. For each test case:
The first line contains an integers n(1n300,000).
The second line contains n integers describing the given array A, the ith integer is Ai(1Ai1,000,000). Between each two adjacent integers there is a white space separated.
 

Output
For each test case, the only line contains the only integer that is the maximum value of the function.
 

Sample Input
132 3 1
 

Sample Output
10
Hint
When and only when l=1,r=2,k=1, the value of the function is the maximum.

官方题解:

先枚举kk,将所有A_{i\times k}Ai×kii是正整数且i\times k<=ni×k<=n)取下来存到B_iBi,于是将原问题转化成了下述问题:对于给定的正整数序列

B_1,B_2,...,B_{\lfloor\frac{n}{k}\rfloor}B1,B2,...,Bkn,求出连续的一段,使得这段的和值乘以这段的最小值的结果最大。

我们可以枚举最小值,设其在第ii位出现,此时我们只要和值最大就可以了。设B_iBi向左第一个小于B_iBi的数是ll(若没有则l=0l=0),向右第一个小于BiBi的数是rr(若没有则r=\lfloor\frac{n}{k}\rfloor+1r=kn+1)。

则保证最小值不变的最大和值是B_{l+1},B_{l+2}...B_{r-1}Bl+1,Bl+2...Br1这段。可以使用单调栈这种数据结构来在O(\lfloor\frac{n}{k}\rfloor)O(kn)的复杂度下计算对于每个iillrr,枚举最小值出现位置及更新答案的复杂度也是

O(\lfloor\frac{n}{k}\rfloor)O(kn)

再考虑外层的枚举k这一部分,总复杂度为O\left(1+\frac{n} {2}+...+\frac{n}{n}\right)O(1+2n+...+nn)O(n\times(1+\frac{1}{2}+...+\frac{1}{n}))O(n×(1+21+...+n1)),由于调和级数1+\frac{1}{2}+...+\frac{1}{n}1+21+...+n1lognlogn级别,因此时间复杂度为O\left(nlogn\right)O(nlogn)


做了这题,还学习了单调栈的使用及其用途


#include <cstdio>#include <algorithm>using namespace std;struct Node {    int index,sta;}s[300005];int a[300005],b[300005],sta[300005],des[300005],n,sqrtK,cnt,top;long long sum[300005],ans;//sum[i]为b数组中区间[1,i]的和int main() {    int T;    scanf("%d",&T);    while(T-->0) {        scanf("%d",&n);        for(int i=1;i<=n;++i) {            scanf("%d",a+i);        }        ans=0;        for(int k=1;k<=n;++k) {//枚举k            sum[0]=cnt=0;            for(int i=k;i<=n;i+=k) {//找到所有的a[l*k],l*k<=n                b[++cnt]=a[i];                sum[cnt]=sum[cnt-1]+b[cnt];            }            b[0]=b[++cnt]=-1;//令最后一个元素的下一个高度为-1,避免循环完毕后还要弹出栈中所有元素            s[0].index=s[0].sta=0;            top=0;            for(int i=1;i<=cnt;++i) {                if(b[i]>=b[s[top].index]) {//若当前元素大于等于栈顶元素                    ++top;                    s[top].index=s[top].sta=sta[i]=i;//其起始下标就是自己的下标                }                else {                    while(b[i]<b[s[top].index]) {                        des[s[top].index]=i-1;//获得以b[s[top].index]为最小值的区间结束的下标                        --top;//弹出栈顶元素                    }                    s[++top].index=i;//其起始下标是弹出的最后一个元素的起始下标                    sta[i]=s[top].sta;//获得以b[s[top].index]为最小值的区间开始的下标                }            }            sqrtK=sqrt(k);            for(int i=1;i<=cnt;++i)//枚举最小值                ans=max(ans,1LL*(sum[des[i]]-sum[sta[i]-1])*b[i]*sqrtK);        }        printf("%I64d\n",ans);    }    return 0;}


0 0
原创粉丝点击