Luogu P2310 loidc,看看海

Posted Ivanovcraft

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P2310 loidc,看看海相关的知识,希望对你有一定的参考价值。

各位大佬都用的排序和杨颙大定理,蒟蒻的我怎么也不会做(瑟瑟发抖),那么,就来一发主席树吧。
我们知道线段树可以维护区间,平衡树可以维护值域
那么,我们可以用线段树套平衡树来解决这个区间值域的问题
线段树套平衡树(令人窒息的操作)
好在权值线段树也可以维护值域,
我们只要建n棵线段树维护前缀和,然后作差就好
考虑空间限制,我们需要让这些线段树共用一些节点,再搞一搞离散化,其实这题的数据范围不用
一颗主席树华丽登场
时间复杂度O(nlogn),空间复杂度O(nlogn)
上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5010;
struct tree{
    int v,ls,rs;
}a[15*maxn];
int n,m,rt[maxn],cnt,mp[maxn],v[maxn],out[maxn];
void insert(int pre,int cur,int p,int l,int r)    //插入
{
    if(l==r)
    {
        a[cur].v=a[pre].v+1;
        return;
    }
    int m=(l+r)>>1;
    if(p<=m)
    {
        a[cur].ls=++cnt;    //新建节点
        a[cur].rs=a[pre].rs;    //共用节点
        insert(a[pre].ls,a[cur].ls,p,l,m);
    }
    else
    {
        a[cur].rs=++cnt;
        a[cur].ls=a[pre].ls;
        insert(a[pre].rs,a[cur].rs,p,m+1,r);
    }
    a[cur].v=a[a[cur].ls].v+a[a[cur].rs].v;    //push up
}
int kth(int x,int y,int k,int l,int r)    //查询k小值
{
    if(l==r)
        return l;
    int m=(l+r)>>1;
    int num=a[a[y].ls].v-a[a[x].ls].v;    //前缀和
    if(num>=k)    //第k小在左子树
        return kth(a[x].ls,a[y].ls,k,l,m);
    else    //在右子树
        return kth(a[x].rs,a[y].rs,k-num,m+1,r);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v[i]);
        mp[i]=v[i];
    }
    sort(mp+1,mp+n+1);    //特色离散化
    cin>>m;
    for(int i=1;i<=n;i++)
    {
        v[i]=lower_bound(mp+1,mp+n+1,v[i])-mp;
        out[v[i]]=i;
        rt[i]=++cnt;
        insert(rt[i-1],rt[i],v[i],1,n);
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        printf("%d\n",out[kth(rt[x-1],rt[y],k,1,n)]);
    }
    return 0;
}

 

以上是关于Luogu P2310 loidc,看看海的主要内容,如果未能解决你的问题,请参考以下文章

合并神犇

P2300 合并神犇 DP

习题:刷图(01背包)

luogu1018乘积最大--区间DP

Luogu P1315 观光公交

BZOJ 1002 && Luogu 2144 [FJOI 2007] 轮状病毒