标签:des blog io ar os sp for on div
真是蛋疼死了……一开始想法有点不对wa来wa去的停不下来,最后又因为log预处理的时候数组开小re了
题意是给定一个序列,要求序列所有子串和中前k大的和,并且限制子串的长度在L到R之间
这题没做过类似的真的有点难
首先令三元组(i,l,r)表示所有左端点在i,右端点在区间[l,r]中的所有子串中sum(i,t)最大的那个t
把这些三元组扔进堆里,然后每次提一个sum(i,t)最大的子串出来,再加入(i,l,t-1)和(i,t+1,r)
对于查找(i,l,r)中的t的操作,就是要求sum(i,t)最大的t,l<=t<=r
显然这个等价于求sum(1,t)前缀和最大
于是又变成在区间[l,r]中查询前缀和的最大值,显然RMQ搞定。这个预处理nlogn,查询O(1)
因为只要做k次,每次最多删去1个加入2个相当于加入1个,所以复杂度nlogn+klog(n+k)
#include<cstdio> #include<iostream> #include<queue> #define N 1500010 #define LL long long #define mkp(a,b,c,d) (dat){a,b,c,d} using namespace std; inline LL read() { LL x=0,f=1;char ch=getchar(); while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f*=-1;ch=getchar();} while (ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return f*x; } struct dat{int l,r,i,t;}; priority_queue <dat,vector<dat> > q; int n,k,L,R; int a[N],s[N]; LL ans; int lg[N],bin[21]; int mx[N][21]; inline bool operator <(const dat &a,const dat &b) {return s[a.t]-s[a.i-1]<s[b.t]-s[b.i-1];} inline void pre() { lg[0]=-1;for (int i=1;i<=n;i++)lg[i]=lg[i>>1]+1; bin[0]=1;for (int i=1;i<=20;i++)bin[i]=bin[i-1]<<1; for (int i=1;i<=n;i++)mx[i][0]=i; for (int i=1;i<=lg[n];i++) for (int j=1;j<=n-bin[i]+1;j++) { int t1=mx[j][i-1],t2=mx[j+bin[i-1]][i-1]; if (s[t1]>s[t2])mx[j][i]=t1;else mx[j][i]=t2; } } inline int query(int l,int r) { if (l==r)return l; int res=lg[r-l+1]; int t1=mx[l][res],t2=mx[r-bin[res]+1][res]; if (s[t1]>s[t2])return t1; else return t2; } int main() { n=read();k=read();L=read();R=read(); for (int i=1;i<=n;i++)a[i]=read(); for (int i=1;i<=n;i++)s[i]=s[i-1]+a[i]; pre(); for (int i=1;i<=n;i++) { if (i+L-1>n)break; int Lt=i+L-1; int Rt=min(i+R-1,n); int fuck=query(Lt,Rt); q.push(mkp(Lt,Rt,i,fuck)); } for (int i=1;i<=k;i++) { dat now=q.top();q.pop(); ans+=(LL)s[now.t]-s[now.i-1]; if (now.t-1>=now.l)q.push(mkp(now.l,now.t-1,now.i,query(now.l,now.t-1))); if (now.r>=now.t+1)q.push(mkp(now.t+1,now.r,now.i,query(now.t+1,now.r))); } printf("%lld\n",ans); }
标签:des blog io ar os sp for on div
原文地址:http://www.cnblogs.com/zhber/p/4160491.html