BZOJ2086 [Poi2010]Blocks

来源:互联网 发布:租自行车软件 编辑:程序博客网 时间:2024/06/05 17:23

容易发现,一段区间满足题目要求的充要条件是区间和>=区间长度*k

做前缀和数组s,这样就得到了以每个点为右端点,1为左端点的区间的和,然后另s[i]等于s[i]-i*k,这样得到差数组d,如果d[i]>=0,那么说明当前以i为右端点是满足题目要求的

每次把左端点右移一位相当于删掉d数组最左边的元素,并且把每个数加上k-a[i]

这样我们只要得出d数组每个位置第一次变得大于等于0是什么,就可以更新答案

于是我们发现如果有d[i]<=d[j]&&i<j,那么i就没有意义了,因为如果i,j同时满足条件那么j一定比i优,而j满足条件不会比i晚

所以我们用单调栈去除没有意义的元素,于是序列变成了单调递减序列,所以变得第一次大于等于0的时间也是单调的,然后把左端点一位一位右移并判断当前最小的右端点是否已经被删除或变得大于等于0即可

友情提示:行末回车会PE,以及,样例弱的要死-_-

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<vector>#include<map>#include<set>#include<bitset>#include<queue>#include<stack>using namespace std;#define MAXN 1000010#define MAXM 1010#define INF 1000000000#define MOD 1000000007#define eps 1e-8#define ll long longint n,m,k;int a[MAXN];ll s[MAXN],d[MAXN];int st[MAXN],tp;ll now;int main(){int i;scanf("%d%d",&n,&m);for(i=1;i<=n;i++){scanf("%d",&a[i]);s[i]=s[i-1]+a[i];}while(m--){scanf("%d",&k);now=tp=0;for(i=1;i<=n;i++){d[i]=s[i]-(ll)k*i;while(tp&&d[st[tp]]<=d[i]){tp--;}st[++tp]=i;}a[0]=k;int ans=0;int wzh=1;for(i=0;i<=n;i++){now+=k-a[i];while(wzh<=tp&&st[wzh]<=i){wzh++;}while(wzh<=tp&&d[st[wzh]]+now>=0){ans=max(ans,st[wzh]-i);wzh++;}}printf(m==0?"%d\n":"%d ",ans);}return 0;}/**/


0 0
原创粉丝点击