bzoj 4504: K个串【大根堆+主席树】

像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上
关于怎么快速求区间和,用可持久化线段树维护(主席树?)每个点到他root的区间和,这样每次右端点右移就是上一个的线段树在(la[a[i]]+1,i)加上a[i],la是这个值a[i]上一次出现的位置
然后就可以在线处理询问了
有一点因为这个线段树建的是1~n,所以右端点不是n的时候取max会取到右端点向右还是初始值0的位置(有可能前面是负数),这样的解决方法就是先全填成-inf,然后每次右移的时候先把右端点加上inf再处理区间加

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
const int N=300005;
int n,m,has,rt[N],tot,la[N];
long long a[N],g[N],rl[N],ans;
map<long long ,int>mp;
struct zxs
{
    int ls,rs,p;
    long long mx,lz;
}t[7000005];
struct qwe
{
    int d,l,r,p;
    long long v;
    qwe(int D=0,int L=0,int R=0,int P=0,long long V=0)
    {
        d=D,l=L,r=R,p=P,v=V;
    }
    bool operator < (const qwe &a) const
    {
        return v<a.v;
    }
};
priority_queue<qwe>q;
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
void build(int &ro,int l,int r)
{
    ro=++tot;
    t[ro].p=l,t[ro].mx=-1e15;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(t[ro].ls,l,mid);
    build(t[ro].rs,mid+1,r);
}
void ud(int ro)
{
    if(t[t[ro].ls].mx>t[t[ro].rs].mx)
        t[ro].mx=t[t[ro].ls].mx,t[ro].p=t[t[ro].ls].p;
    else
        t[ro].mx=t[t[ro].rs].mx,t[ro].p=t[t[ro].rs].p;
}
void update(int &ro,int la,int l,int r,int ll,int rr,long long v,long long lz)
{
    ro=++tot;
    t[ro]=t[la];
    t[ro].lz+=lz;
    t[ro].mx+=lz;
    if(l==ll&&r==rr)
    {
        t[ro].lz+=v;
        t[ro].mx+=v;
        return;
    }
    int mid=(l+r)>>1;
    if(t[ro].lz)
    {
        if(rr<=mid)
        {
            t[ro].rs=++tot;
            t[t[ro].rs]=t[t[la].rs];
            t[t[ro].rs].mx+=t[ro].lz;
            t[t[ro].rs].lz+=t[ro].lz;
            update(t[ro].ls,t[la].ls,l,mid,ll,rr,v,t[ro].lz);
        }
        else if(ll>mid)
        {
            t[ro].ls=++tot;
            t[t[ro].ls]=t[t[la].ls];
            t[t[ro].ls].mx+=t[ro].lz;
            t[t[ro].ls].lz+=t[ro].lz;
            update(t[ro].rs,t[la].rs,mid+1,r,ll,rr,v,t[ro].lz);
        }
        else
        {
            update(t[ro].ls,t[la].ls,l,mid,ll,mid,v,t[ro].lz);
            update(t[ro].rs,t[la].rs,mid+1,r,mid+1,rr,v,t[ro].lz);
        }
        t[ro].lz=0;
    }
    else
    {
        if(rr<=mid)
            update(t[ro].ls,t[la].ls,l,mid,ll,rr,v,0);
        else if(ll>mid)
            update(t[ro].rs,t[la].rs,mid+1,r,ll,rr,v,0);
        else
        {
            update(t[ro].ls,t[la].ls,l,mid,ll,mid,v,0);
            update(t[ro].rs,t[la].rs,mid+1,r,mid+1,rr,v,0);
        }
    }
    ud(ro);
}
pair<long long,int> ques(int ro,int l,int r,int ll,int rr)
{//cerr<<l<<" "<<r<<"   "<<ll<<" "<<rr<<endl;
    if(l==ll&&r==rr)
        return make_pair(t[ro].mx,t[ro].p);
    if(t[ro].lz)
    {
        t[tot+1]=t[t[ro].ls];
        t[tot+1].mx+=t[ro].lz;
        t[tot+1].lz+=t[ro].lz;
        t[ro].ls=tot+1;
        t[tot+2]=t[t[ro].rs];
        t[tot+2].mx+=t[ro].lz;
        t[tot+2].lz+=t[ro].lz;
        t[ro].rs=tot+2;
        tot+=2;
        t[ro].lz=0;
    }
    int mid=(l+r)>>1;
    if(rr<=mid)
        return ques(t[ro].ls,l,mid,ll,rr);
    else if(ll>mid)
        return ques(t[ro].rs,mid+1,r,ll,rr);
    else
    {
        pair<long long,int>a=ques(t[ro].ls,l,mid,ll,mid),b=ques(t[ro].rs,mid+1,r,mid+1,rr);
        return (a.first>b.first)?a:b;
    }
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        a[i]=g[i]=read();
    sort(g+1,g+1+n);
    for(int i=1;i<=n;i++)
        if(i==1||g[i]!=g[i-1])
            mp[g[i]]=++has,rl[has]=g[i];
    build(rt[0],1,n);
    for(int i=1;i<=n;i++)
    {
        update(rt[i],rt[i-1],1,n,i,i,1e15,0);
        update(rt[i],rt[i],1,n,la[mp[a[i]]]+1,i,a[i],0);
        la[mp[a[i]]]=i;//cerr<<"OK"<<endl;
    }
    for(int i=1;i<=n;i++)
        q.push(qwe(i,1,i,t[rt[i]].p,t[rt[i]].mx));
    while(m--)
    {
        qwe u=q.top();
        q.pop();
        ans=u.v;//cerr<<ans<<endl;
        if(u.l<=u.p-1)
        {
            pair<long long,int>nw=ques(rt[u.d],1,n,u.l,u.p-1);
            q.push(qwe(u.d,u.l,u.p-1,nw.second,nw.first));
        }
        if(u.p+1<=u.r)
        {
            pair<long long,int>nw=ques(rt[u.d],1,n,u.p+1,u.r);
            q.push(qwe(u.d,u.p+1,u.r,nw.second,nw.first));
        }
    }
    printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/10659467.html

时间: 2024-11-07 01:01:40

bzoj 4504: K个串【大根堆+主席树】的相关文章

BZOJ 2006 NOI 2010 超级钢琴 堆+主席树

题目大意:给出一些音符,将它们组成和旋.和旋只能由[l,r]个音符组成.优美程度为所有音符的和.求k个和旋的又优美程度的最大和. 思路:先处理出来前缀和,以便O(1)去除一段的和.然后考虑对于一个音符来说,向左边扩展的音符是一段长度为r - l + 1的区间,取出的最大和是sum[i] - sum[p],sum[i]是一定的,要想让整段和最大,需要让sum[p]最小.之后就是区间k小值和堆得维护了,可以用时代的眼泪划分树,也可以用主席树. CODE: #include <vector> #in

[bzoj P4504] K个串

[bzoj P4504] K个串 [题目描述] 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第k大的和是多少. [输入格式] 第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和 接下里一行n个数a_i,表示这个数字序列 [输出格式] 一行一个整数,表示第k大的和 [样例输入] 7 5

4504: K个串 主席树+优先队列

这道题因为有一个数在序列中出现多次只算一次的限制.我们可以这样搞.假设在当前题意下求给定右端点的区间最值.那么我们可以预处理出每个数前一次出现的位置pre[i] .接下来从左到右加入每一个值,就是在 pre[i] + 1 —— i 这个区间内加上 v[i] 的值,这样就可以得到以当前 i 点为右端点的各个区间的值(很明显维护一下最值就好了).接下来很明显有n个版本的线段树(如果你说一开始那个空的线段树也算一个版本的话,就有n+1个),那就要用主席树动态开点.而取第K大值的操作有点像超级钢琴,不过

BZOJ 2006 超级钢琴(堆+主席树)

很好的一道题. 题意:给出长度为n的数列,选择k个互不相同的区间,满足每个区间长度在[L,R]内,求所有选择的区间和的总和最大是多少.(n,k<=5e5). 首先将区间和转化为前缀和之差,那么我们想要区间和的总和最大,一个朴素的想法是把所有满足限制的区间和排序,取最大的k个. 考虑每个右端点i,其中有效的左端点是[i-R+1,i-L+1].如果我们对于每个右端点都找到"当前"最大的区间和,那么把它们扔进大根堆里维护一下即可. 由于sum[i]一定,那么只要左端点的sum值越小越好

bzoj 4919: [Lydsy六月月赛]大根堆

Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Solution 思路比较直接 设 \(f[i][j]\) 表示\(i\)为子树的节点中,堆中最大值小于\(j\)的情况下能选的最多点数 转移时就是用一个前缀最大值更

【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】

题目链接: TP 题解:   可能是我比较纱布,看不懂题解,只好自己想了…… 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LCA一定是z的祖先(这里说的祖先包括自己,以下祖先均为此概念)节点,也就是是说我们只要计算出每个祖先节点的贡献就可以了,再考虑每个祖先的贡献如何计算. 我们发现对于深度其实是该点到root的路径点数,所以我们可以这样想,我们询问z的祖先的答案,就是在计算有对于给定区间有多少个点经过了z的祖先. 那么思路到这里就很清晰了,我们先把每个点

【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并

[BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Input 第一行包含一个正整数n(1<=n<=200000),表示节点的个数. 接下来n行,每行两个整数v

BZOJ 1878 [SDOI2009]HH的项链 (主席树 或 莫队算法)

题目链接  HH的项链 这道题可以直接上主席树的模板 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 5e4 + 10; const int M = 3e6 + 10

bzoj 2733: [HNOI2012]永无乡 离线+主席树

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1167  Solved: 607[Submit][Status] Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a