Luogu P1533 可怜的狗狗

7 篇文章 0 订阅
5 篇文章 0 订阅

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

 

 线段树版

主席树版呵呵

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值