https://www.luogu.org/problemnew/show/P1533
题目背景
小卡由于公务需要出差,将新家中的狗狗们托付给朋友嘉嘉,但是嘉嘉是一个很懒的人,他才没那么多时间帮小卡喂狗狗。
题目描述
小卡家有N只狗,由于品种、年龄不同,每一只狗都有一个不同的漂亮值。漂亮值与漂亮的程度成反比(漂亮值越低越漂亮),吃饭时,狗狗们会按顺序站成一排等着主人给食物。
可是嘉嘉真的很懒,他才不肯喂这么多狗呢,这多浪费时间啊,于是他每次就只给第i只到第j只狗中第k漂亮的狗狗喂食(好狠心的人啊)。而且为了保证某一只狗狗不会被喂太多次,他喂的每个区间(i,j)不互相包含。
输入输出格式
输入格式:
第一行输入两个数n,m,你可以假设n<300001 并且 m<50001;m表示他喂了m次。
第二行n个整数,表示第i只狗的漂亮值为ai。
接下来m行,每行3个整数i,j,k表示这次喂食喂第i到第j只狗中第k漂亮的狗的漂亮值。
输出格式:
M行,每行一个整数,表示每一次喂的那只狗漂亮值为多少。
输入输出样例
输入样例#1: 复制
7 2
1 5 2 6 3 7 4
1 5 3
2 7 1
输出样例#1: 复制
3
2
先要离散化
因为区间互不包含,所以若l是单调递增,r也一定单调递增,所以果断排序
排序后用队列维护选的数,要从中选取第K大的,权值树状数组+倍增,好写又跑的快
O(n lg n)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+4;
int n,m,b[N],now,ans[N],L,R,bb[N],c[N];
struct A{ int id,x; }a[N];
bool cmp(A i,A j){ return i.x<j.x; }
struct B{ int id,l,r,k; }q[N];
bool cmp1(B i,B j){ return i.l<j.l; }
inline void add(int x,int k)
{
for(int i=x;i<=n;i+=i&-i) c[i]+=k;
}
struct C
{
int c[N];
}tree;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
a[i].id=i;
scanf("%d",&a[i].x);
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
b[a[i].id]=i;
bb[i]=a[i].x;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
q[i].id=i;
}
sort(q+1,q+m+1,cmp1);
L=1,R=0;
for(int i=1;i<=m;i++)
{
while(R<q[i].r)
{
R++;
add(b[R],1);
}
while(L<q[i].l)
{
add(b[L],-1);
L++;
}
now=0;
for(int j=18;j>=0;j--)
if(now+(1<<j)<=n)
if(c[now+(1<<j)]<q[i].k)
{
now+=(1<<j);
q[i].k-=c[now];
}
if(q[i].k>0) now++;
ans[q[i].id]=bb[now];
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
权值线段树:把权值树状数组+二分改成权值线段树
#include<cstdio>
#include<algorithm>
using namespace std;
int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret;
}
const int N=2e6+4;
int n,m,b[N],l,r,mid,ans[N],s,ll,rr,bb[N];
struct A{
int id,x;
}a[N];
struct B{
int id,l,r,k;
}q[N];
bool cmp(A i,A j)
{
return i.x<j.x;
}
bool cmp1(B i,B j)
{
return i.l<j.l;
}
struct C
{
int c[N];
void add(int p,int l,int r,int x,int k)
{
if(l==r)
{
c[p]+=k; return;
}
int mid=l+r>>1;
if(mid>=x) add(p<<1,l,mid,x,k);
else add(p<<1|1,mid+1,r,x,k);
c[p]=c[p<<1]+c[p<<1|1];
}
int find(int p,int l,int r,int ned)
{
if(l==r) return l;
int mid=l+r>>1;
if(c[p<<1]<ned) return find(p<<1|1,mid+1,r,ned-c[p<<1]);
else return find(p<<1,l,mid,ned);
}
}tree;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=(A){i,read()};
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) b[a[i].id]=i,bb[i]=a[i].x;
for(int i=1;i<=m;i++) q[i]=(B){i,read(),read(),read()};
sort(q+1,q+m+1,cmp1);
ll=1,rr=0;
for(int i=1;i<=m;i++)
{
while(rr<q[i].r)
rr++,tree.add(1,1,n,b[rr],1);
while(ll<q[i].l)
tree.add(1,1,n,b[ll],-1),ll++;
ans[q[i].id]=bb[tree.find(1,1,n,q[i].k)];
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
treap强上,连离散化都不用了!
#include<cstdio>
#include<algorithm>
#include<ctime>
using namespace std;
const int N=1e6+5;
int n,m,a[N],l,r,ans[N];
struct A{int id,u,v,k; }q[N];
bool cmp(A i,A j)
{
return i.u<j.u;
}
struct B
{
int r,hea[N],tre[N],lc[N],rc[N],s[N],c[N],cnt;
inline void zig(int &x,int y)
{
lc[x]=rc[y], rc[y]=x;
s[y]=s[x],s[x]=s[lc[x]]+s[rc[x]]+c[x];
x=y;
}
inline void zag(int &x,int y)
{
rc[x]=lc[y],lc[y]=x;
s[y]=s[x],s[x]=s[lc[x]]+s[rc[x]]+c[x];
x=y;
}
void ins(int &x,int k)
{
if(!x)
{
x=++cnt,hea[x]=rand(),tre[x]=k,c[x]=s[x]=1;
return;
}
s[x]++;
if(tre[x]==k)c[x]++;
else if(tre[x]>k)
{
ins(lc[x],k);
if(hea[lc[x]]<hea[x]) zig(x,lc[x]);
}else
{
ins(rc[x],k);
if(hea[rc[x]]<hea[x]) zag(x,rc[x]);
}
}
void del(int &x,int k)
{
if(k==tre[x])
{
if(c[x]>1) c[x]--,s[x]--;
else if(!lc[x]||!rc[x]) x=lc[x]+rc[x];
else if(hea[lc[x]]<hea[rc[x]]) zig(x,lc[x]),del(x,k);
else zag(x,rc[x]),del(x,k);
return;
}
s[x]--;
if(k<tre[x]) del(lc[x],k);
else del(rc[x],k);
}
int kth(int x,int k)
{
if(s[lc[x]]<k&&s[lc[x]]+c[x]>=k) return tre[x];
if(s[lc[x]]>=k) return kth(lc[x],k);
return kth(rc[x],k-s[lc[x]]-c[x]);
}
}treap;
int main()
{
srand(time(NULL));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
q[i].id=i,scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].k);
sort(q+1,q+m+1,cmp);
l=1; r=1;
for(int i=1;i<=m;i++)
{
while(q[i].v>=r) treap.ins(treap.r,a[r]),r++;
while(q[i].u>l) treap.del(treap.r,a[l]),l++;
ans[q[i].id]=treap.kth(treap.r,q[i].k);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
主席树模板题
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=6e6+5,M=3e5+5;
int n,m,rt[M],b[M];
struct A{int id,x; }a[M];
bool cmp(A i,A j)
{
return i.x<j.x;
}
struct B
{
int cnt,lc[N],rc[N],s[N];
void add(int &p,int l,int r,int x)
{
lc[++cnt]=lc[p],rc[cnt]=rc[p],s[cnt]=s[p]+1,p=cnt;
if(l==r) return;
int mid=l+r>>1;
if(x<=mid) add(lc[p],l,mid,x);
else add(rc[p],mid+1,r,x);
}
int kth(int p1,int p2,int l,int r,int k)
{
if(l==r) return l;
int xx=s[lc[p2]]-s[lc[p1]];
int mid=l+r>>1;
if(k<=xx) return kth(lc[p1],lc[p2],l,mid,k);
return kth(rc[p1],rc[p2],mid+1,r,k-xx);
}
}zxs;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].x),a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++) b[a[i].id]=i;
for(int i=1;i<=n;i++)
rt[i]=rt[i-1],zxs.add(rt[i],1,n,b[i]);
while(m--)
{
int l,r,k; scanf("%d%d%d",&l,&r,&k);
printf("%d\n",a[zxs.kth(rt[l-1],rt[r],1,n,k)].x);
}
return 0;
}
作为一个有觉悟的Man,比较了两种方法:
树状数组<treap<线段树<主席树
树状数组版
其次是treap
线段树版
主席树版呵呵