您的位置:首页 > 其它

BZOJ2006: [NOI2010]超级钢琴

2017-04-23 19:32 387 查看
求和为前k大的长为L~R的区间的和

将区间和转前缀和∑Ri=Lai=s[R]−s[L−1]

右端点相同的区间放一起,以他们的区间和的最大值为代表值,把这些右端点放进一个堆里,取k次,每次取区间和最大的右端点出来,将它的左端点区间以最小的s[L-1]的L为界,取出这个L,剩下的分成两部分,求出这两部分s[L-1]的最小值,算出各自最大的区间和,把他们放进堆里

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void swap(int &x,int &y){x^=y;y^=x;x^=y;}
inline void read(int &x)
{
char c; int f=1;
while(!((c=getchar())>='0'&&c<='9')) if(c=='-') f=-1;
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
x*=f;
}
const int maxn = 510000;
const int maxb = 21;

int n,m,L,R,a[maxn];
ll s[maxn];
int mn[maxn][maxb];

int get_mn(int l,int r)
{
ll re=LLONG_MAX; int pos;
for(int i=maxb-1;i>=0;i--) if(r-(1<<i)+1>=l)
{
if(re>s[mn[r][i]]) pos=mn[r][i],re=s[pos];
r-=1<<i;
}
return pos;
}

struct node
{
ll c;
int l,r,i,p;
node(){}
node(const ll _c,int _l,int _r,int _i,int _p){c=_c;l=_l;r=_r;i=_i;p=_p;}
};
bool operator <(node x,node y){return x.c<y.c;}
priority_queue<node>Q;

int main()
{
read(n); read(m); read(L); read(R);
for(int i=1;i<=n;i++) read(a[i]),s[i]=s[i-1]+(ll)a[i];

for(int i=0;i<=n;i++) mn[i][0]=i;
for(int j=1;j<maxb;j++)
{
for(int i=1;i<=n;i++)
{
int la=i-(1<<j-1);
if(la<0) { mn[i][j]=mn[i][j-1]; continue; }
if(s[mn[i][j-1]]<s[mn[la][j-1]])
mn[i][j]=mn[i][j-1];
else mn[i][j]=mn[la][j-1];
}
}

for(int i=L;i<=n;i++)
{
int l=max(0,i-R),r=i-L;
int p=get_mn(l,r);
node tmp=node(s[i]-s[p],l,r,i,p);
Q.push(tmp);
}

ll ans=0;
while(m--)
{
node x=Q.top(); Q.pop();
ans+=x.c;
int l=x.l,r=x.p-1,p=get_mn(l,r);
if(l<=r) Q.push(node(s[x.i]-s[p],l,r,x.i,p));
l=x.p+1,r=x.r,p=get_mn(l,r);
if(l<=r) Q.push(node(s[x.i]-s[p],l,r,x.i,p));
}
printf("%lld\n",ans);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: