标签: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