JZOJ 5409 【NOIP2017提高A组集训10.21】Fantasy

来源:互联网 发布:投诉淘宝卖家电话 编辑:程序博客网 时间:2024/05/29 11:21

Fantasy

Description

给出一个长度为n的序列A
对于这个序列的每一个子串,定义其价值为这个子串的和。
问选择K个不同的子串的价值之和的最大值。
其中题目有一个特殊限制,选择出来的这些子串的长度必须在LR之间。

Data Constraint

1<=n,K<=105 1<=L<R<=N, |Ai|<=104

Solution

方法一: O(K log n)
求出序列的前缀和q,对于一段区间(l,r)的价值可以表示成qr-ql1
RMQ求出区间前缀最大值及最大值的位置。
用一个三元组(z,l,r)表示左端点为z,右端点的范围为[l,r]时的最大价值,同时记录一下这个三元组的右端点取到哪个点时价值最大。
将这样(n-L+1)个三元组丢到一个set中。
执行下列操作K次,每一次取出 价值最大的三元组,设这个三元组为(z,l,r),取到第d个点时价值最大,将价值(即qd-qz1)加入到答案中,然后将三元组(z,l,d-1)和三元组(z,d+1,r)丢入到set维护即可。

这个算法的缺点在于它的复杂度与K有关,当K很大的时候就跑不过了,因而介绍第二种复杂度与K无关的算法。

方法二:O(n log2n)
二分第K大的子串价值,从右往左扫用一棵权值线段树后缀值在某段区间内 的个数,求出大于二分价值的子串个数,继而调整缩小二分的范围。
对于单次的二分的时间复杂度是O(n logn),因而总的时间复杂度为O(n log2n)。

Code of Solution 1

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<set>#include<cmath>#define fo(i,j,l) for(int i=j;i<=l;i++)#define fd(i,j,l) for(int i=j;i>=l;i--)using namespace std;typedef long long ll;const ll N=11e4,M=21;struct note{ll s,k,t,l,r;};struct myComp  {      bool operator()(note a,note b)      {        return a.s!=b.s ? a.s>b.s : (a.k!=b.k ? a.k<b.k : a.t<b.t) ;      }  };set<note,myComp> op;int n,m,j,k,l,i,le,ri;ll a[N],q[N];ll f[N][M],m2[M],fl[2*N];int min(ll a,ll b){if(a<b)return a;else return b;}note merge(int o,int l,int r){    int yy=fl[r-l+1];    note u; u.k=o; u.l=l; u.r=r;    if(q[f[u.l][yy]]>=q[f[u.r-m2[yy]+1][yy]])u.t=f[u.l][yy];    else u.t=f[u.r-m2[yy]+1][yy];    u.s=q[u.t]-q[o]; return u;}void read(ll &o){    o=0; char ch=' ';    for(;(ch<'0'||ch>'9')&&(ch!='-');)ch=getchar();    ll u=1;    if(ch=='-')u=-1,ch=getchar();    for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;    o*=u;}int main(){    freopen("fantasy.in","r",stdin);    freopen("fantasy.out","w",stdout);    cin>>n>>k>>le>>ri;    m2[0]=1;    fo(i,1,18)m2[i]=m2[i-1]*2;    for(int i=0;m2[i]<=n;i++)    fo(l,m2[i],m2[i+1]-1) fl[l]=i;    fl[1]=0;    q[0]=a[0]=0;    fo(i,1,n)read(a[i]),q[i]=q[i-1]+a[i];    int y=fl[n];    fo(i,1,n)f[i][0]=i;    fo(i,1,y)    fo(l,1,n-m2[i]+1)    if(q[f[l][i-1]]<=q[f[l+m2[i-1]][i-1]])f[l][i]=f[l+m2[i-1]][i-1];    else f[l][i]=f[l][i-1];    fo(i,1,n-le+1)op.insert(merge(i-1,i+le-1,min(n,i+ri-1)));    ll ans=0;    fo(i,1,k){        note u=*op.begin();         ans+=u.s;        int z1=u.l,z2=u.t-1,y1=u.t+1,y2=u.r;        op.erase(op.begin());        if(z1<=z2)op.insert(merge(u.k,z1,z2));        if(y1<=y2)op.insert(merge(u.k,y1,y2));    }    printf("%lld\n",ans);}
阅读全文
1 0
原创粉丝点击