Bzoj2086 [Poi2010]Blocks
Description
给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于k。
总共给出M次询问,每次询问给出的k不同,你需要分别回答。
Input
第一行两个正整数N (N <= 1,000,000)和M (M <= 50)。
第二行N个正整数,第i个正整数表示a[i] (a[i] <= 10^9)。
第三行M个正整数,第i个正整数表示第i次询问的k (k <= 10^9)。
Output
共一行,输出M个正整数,第i个数表示第i次询问的答案。
Sample Input
5 6
1 2 1 1 5
1 2 3 4 5 6
1 2 1 1 5
1 2 3 4 5 6
Sample Output
5 5 2 1 1 0
Bzoj权限题。目前只测了样例。
和操作次数没有关系。分析一波可以知道,只要一段的平均数大于k,就有解。
维护一个前缀和数组(累加每一项时都减去k),然后在这个数组里维护一个单增的单调栈求解。
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #define LL long long 7 using namespace std; 8 const int mxn=1000020; 9 int n,m; 10 int a[mxn];LL sum[mxn]; 11 int k; 12 int ans; 13 int st[mxn],top; 14 void solve(){ 15 int i; 16 for(i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]-k; 17 top=0; 18 for(i=1;i<=n;i++)if(sum[st[top]]>sum[i])st[++top]=i;//前缀和更大,则入栈 19 for(i=n;i>=0;i--){ 20 while(top && (sum[i]>=sum[st[top-1]]))top--;//长度尽可能大 21 ans=max(ans,i-st[top]); 22 } 23 return; 24 } 25 int main(){ 26 scanf("%d%d",&n,&m); 27 int i,j; 28 for(i=1;i<=n;i++) scanf("%d",&a[i]); 29 for(i=1;i<=m;i++){ 30 scanf("%d",&k); 31 ans=0; 32 solve(); 33 printf("%d ",ans); 34 } 35 return 0; 36 }
本文为博主原创文章,转载请注明出处。