hdu6278 (二分+主席树或者莫队+二分树状数组)

题意:查询区间l,r中最的大x,满足区间中大于等于x的的数量大于x。

思路:二分答案,然后主席树查询第(r-l+1)-mid+1大和mid比较即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int ran[100005],root[100005];

struct node{
	int val,id;
}a[100050];
int cmp(node q,node w)
{
	return q.val<w.val;
}
struct TR{
	int sum,l,r;
}tree[100050*36];
int cnt;
void init()
{
	tree[0].l=tree[0].r=tree[0].sum=0;cnt=1;
}
void build(int l,int r,int &rt,int val)
{
	tree[cnt++]=tree[rt];
	rt=cnt-1;
	tree[rt].sum++;
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(val<=mid) build(l,mid,tree[rt].l,val);
	else build(mid+1,r,tree[rt].r,val);
}
int query(int i,int j,int k,int l,int r)
{
	int d=tree[tree[j].l].sum-tree[tree[i].l].sum;
	if(l==r) return l;
	int mid=(l+r)>>1;
	if(k<=d) return query(tree[i].l,tree[j].l,k,l,mid);
	else return query(tree[i].r,tree[j].r,k-d,mid+1,r);
}
int main()
{
	int n,q;
	while(cin>>n>>q)
	{
		init();
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].val;a[i].id=i;
		}
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++) ran[a[i].id]=i;
		for(int i=1;i<=n;i++)
		{
			root[i]=root[i-1];
			build(1,n,root[i],ran[i]);
		}
		while(q--)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			int L=0,R=(r-l+1);int ans=0;
			while(L<=R)
			{
				int mid=(L+R)>>1;
				int op=a[query(root[l-1],root[r],r-l-mid+2,1,n)].val;//第mid大的数字 
				if(op>=mid) 
				{
					L=mid+1;
					ans=max(ans,mid);
				}
				else R=mid-1;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

当然也可以在查询过程中判断当前节点是否满足题意,可以省去二分的过程。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int root[100005];

struct node{
	int val;
}a[100050];
int cmp(node q,node w)
{
	return q.val<w.val;
}
struct TR{
	int sum,l,r;
}tree[100050*36];
int cnt;
void init()
{
	tree[0].l=tree[0].r=tree[0].sum=0;cnt=1;
}
void build(int l,int r,int &rt,int val)
{
	tree[cnt++]=tree[rt];
	rt=cnt-1;
	tree[rt].sum++;
	if(l>=r) return ;
	int mid=(l+r)>>1;
	if(val<=mid) build(l,mid,tree[rt].l,val);
	else build(mid+1,r,tree[rt].r,val);
}
int query(int i,int j,int k,int l,int r)
{
	if(l>=r) return l;
	int mid=(l+r)>>1;
	int num=tree[tree[j].r].sum-tree[tree[i].r].sum+k;	//左区间的数,mid
	if(num<=mid) return query(tree[i].l,tree[j].l,num,l,mid);
	else return query(tree[i].r,tree[j].r,k,mid+1,r);
}
int main()
{
	int n,q;
	while(cin>>n>>q)
	{
		init();
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].val;
		}
		for(int i=1;i<=n;i++)
		{
			root[i]=root[i-1];
			build(1,n,root[i],a[i].val);
		}
		while(q--)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%d\n",query(root[l-1],root[r],0,1,n));
		}
	}
	return 0;
}

当然也可以分块+二分树状数组

#include <bits/stdc++.h>
using namespace std;

struct node{
	int l,r,id;
}a[100005];
int block[100005];
int cmp(node q,node w)
{
	if(block[q.l]==block[w.l]) return q.r<w.r;
	return block[q.l]<block[w.l];
}
int ans[100005];
int n,q,b[100005];
int tr[100005];
int lowbit(int x)
{
	return x&-x;
}

void add(int x,int y)
{
	while(x<=n)
	{
		tr[x]+=y;
		x+=lowbit(x);
	}
	return ;
}

int Sum(int x)
{
	int sum=0;
	while(x>0)
	{
		sum+=tr[x];
		x-=lowbit(x);
	}
	return sum;
}

int tot;
int check(int x)
{
	int p=tot-Sum(x-1);
	if(p>=x) return 1;
	return 0;
}
int solve()
{
	int l=0,r=n;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1;
		else r=mid-1;
	}
	return r;
}

int main()
{
	while(scanf("%d%d",&n,&q)!=EOF)
	{
		memset(tr,0,sizeof(tr));
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);
		for(int i=0;i<q;i++)
		{
			scanf("%d%d",&a[i].l,&a[i].r);a[i].id=i;
		}
		
		int t=sqrt(n);
		
		for(int i=0; i<n; i++)
			block[i]=i/t;
		sort(a,a+q,cmp);
		
		int l=1,r=1;add(b[1],1);
		for(int i=0;i<q;i++)
		{
			while(l<a[i].l)
			{
				add(b[l++],-1); 
			}
			while(l>a[i].l)
			{
				add(b[--l],1);
			}
			while(r>a[i].r)
			{
				add(b[r--],-1);
			}
			while(r<a[i].r)
			{
				add(b[++r],1);
			}
			tot=a[i].r-a[i].l+1;
			ans[a[i].id]=solve();
		}
		for(int i=0;i<q;i++)
		{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}
发布了155 篇原创文章 · 获赞 32 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_37632935/article/details/87919230