主席树总结
主席树总结
自己的板子。。。
随便糊一下
想一想,求i到j中的第k大,是不是就是前j个数把前i个数的影响去掉之后的第k大值(排序实现,但不是暴力...)
……(这不是废话?但这道题时刻记住这一种理解方式有利于理解可持久化线段树的实现)
那么先建一棵主席树,然后只要查询两个时期的线段树,然后找到正好加入的时间点就ojbk了
说的很简单,至于实现,那就自己模拟研究......
code
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<ctime>
#include<queue>
#include<stack>
#define rg register
#define il inline
#define lst long long
#define N 200050
using namespace std;
int n,Q,cnt;
int rank[N],root[N];
struct NUMS{
int v,num;
}jlr[N];
struct TREE{
int v,ls,rs;
}ljl[N*20];
il int read()
{
rg int s=0,m=1;rg char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')m=-1,ch=getchar();
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
return s*m;
}
il int cmp(rg const NUMS &a,rg const NUMS &b){return a.v<b.v;}
void Update(rg int &now,rg int ll,rg int rr,rg int xx)
// 当前节点, 左边界, 右边界 ,加入的值
//其实这个函数都是板子
{
ljl[++cnt]=ljl[now];
now=cnt;
ljl[now].v++;
if(ll==rr)return;
rg int mid=(ll+rr)>>1;
if(xx<=mid)Update(ljl[now].ls,ll,mid,xx);
else Update(ljl[now].rs,mid+1,rr,xx);
}
int Query(rg int u,rg int v,rg int ll,rg int rr,rg int kk)
//在ll-1版本上的u点,在rr版本上的v点,左边界,右边界,要找的标号
{
if(ll==rr)return ll;
rg int num=ljl[ljl[v].ls].v-ljl[ljl[u].ls].v;//(以左孩子为例)
//Rank指的是v时间的线段树和u时期的线段树之间加了几个点(我们要找中间第k个加入的点)
rg int mid=(ll+rr)>>1;//递归中"二分"地找
if(kk<=num)return Query(ljl[u].ls,ljl[v].ls,ll,mid,kk);
//如果左孩子里面加了比 要找的序号 数量更多的数,去左孩子上find啊(右孩子的类推)
else return Query(ljl[u].rs,ljl[v].rs,mid+1,rr,kk-num);//算上左孩子上加入的数量造成的贡献
}
int main()
{
n=read(),Q=read();
for(rg int i=1;i<=n;++i)jlr[i].v=read(),jlr[i].num=i;
sort(jlr+1,jlr+n+1,cmp);
for(rg int i=1;i<=n;++i)rank[jlr[i].num]=i;//离散化一波
for(rg int i=1;i<=n;++i)
{
root[i]=root[i-1];//按时间顺序建一棵主席树
Update(root[i],1,n,rank[i]);//把节点都加进去,模拟一下"更新"函数的过程
}
for(rg int i=1;i<=n;++i)
{
rg int ll=read(),rr=read(),k=read();
printf("%d\n",jlr[Query(root[ll-1],root[rr],1,n,k)].v);
}
return 0;
}
题单(来自XZY)
哪怕人间是炼狱,梦想永远是天堂
继续走下去吧,理想永远都年轻,花儿一定会再次盛开