【XSY2720】区间第k小 整体二分 可持久化线段树

题目描述

  给你你个序列,每次求区间第(k)小的数。

  本题中,如果一个数在询问区间中出现了超过(w)次,那么就把这个数视为(n)

  强制在线。

  (nleq 100000,a_i<n,wleq n)

题解

  考虑整体二分。

  先看看离线要怎么做。

  现在我们要计算每个数对每个区间的贡献。

  对于每个询问区间和每种数,让这个区间最右边(w)个数对这个询问的贡献为(1),第(w+1)个数对这个询问的贡献为(-w)

  这样每个数的贡献就是二维平面上的一个矩形。可以用扫描线+线段树解决。

  时间复杂度:(O(nlog n))

  但问题是强制在线。

  可以把这棵线段树可持久化。

  时间复杂度不变。

  总时间复杂度:(O(nlog^2 n))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#include<utility>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
namespace sgt
{
	struct node
	{
		int ls,rs,v;
	};
	node a[50000010];
	int cnt;
	int insert(int p1,int l,int r,int v,int L,int R)
	{
		int p=++cnt;
		a[p]=a[p1];
		if(l<=L&&r>=R)
		{
			a[p].v+=v;
			return p;
		}
		int mid=(L+R)>>1;
		if(l<=mid)
			a[p].ls=insert(a[p].ls,l,r,v,L,mid);
		if(r>mid)
			a[p].rs=insert(a[p].rs,l,r,v,mid+1,R);
		return p;
	}
	int query(int p,int x,int L,int R)
	{
		if(L==R)
			return a[p].v;
		int s=a[p].v;
		int mid=(L+R)>>1;
		if(x<=mid)
			s+=query(a[p].ls,x,L,mid);
		else
			s+=query(a[p].rs,x,mid+1,R);
		return s;
	}
}
struct change
{
	int x,y1,y2,k,w;
	change(){}
	change(int a,int c,int d,int e,int f)
	{
		x=a;
		y1=c;
		y2=d;
		k=e;
		w=f;
//		printf("%d %d %d %d %d
",x,y1,y2,k,w);
	}
};
int cmp(change a,change b)
{
	return a.x>b.x;
}
change c[1000010],c2[1000010];
int cnt;
int n,w,q,type;
int a[100010];
set<int> st[100010];
int rtcnt=0;
int ls[3000010];
int rs[3000010];
int crt;
vector<pii> d[3000010];
int build(int l,int r,int vl,int vr)
{
	if(vl==vr)
		return 0;
	int rr=++rtcnt;
	d[rr].push_back(pii());
	int now=0;
	int i;
	int vm=(vl+vr)>>1;
	int num=0;
	int cnt1=0;
	for(i=l;i<=r;i++)
	{
		if(i!=l&&c[i].x!=c[i-1].x&&num)
		{
			d[rr].push_back(pii(c[i-1].x,now));
			num=0;
		}
		if(c[i].k<=vm)
		{
			now=sgt::insert(now,c[i].y1,c[i].y2,c[i].w,1,n);
			num++;
			cnt1++;
		}
	}
	if(num)
		d[rr].push_back(pii(c[r].x,now));
	int l1=l,r1=l+cnt1;
	for(i=l;i<=r;i++)
		if(c[i].k<=vm)
			c2[l1++]=c[i];
		else
			c2[r1++]=c[i];
	for(i=l;i<=r;i++)
		c[i]=c2[i];
	ls[rr]=build(l,l+cnt1-1,vl,vm);
	rs[rr]=build(l+cnt1,r,vm+1,vr);
	return rr;
}
int get(vector<pii> &s,int x)
{
	if(s.size()==1)
		return 0;
	if(x>s[1].first)
		return 0;
	int l=1,r=s.size()-1;
	while(l<r)
	{
		int mid=(l+r+1)>>1;
		if(x>s[mid].first)
			r=mid-1;
		else
			l=mid;
	}
	return l;
}
int query(int rr,int l,int r,int k,int vl,int vr)
{
	if(vl==vr)
		return vl;
	int p=get(d[rr],l);
	int rt=d[rr][p].second;
	int s=sgt::query(rt,r,1,n);
	int vm=(vl+vr)>>1;
	if(k<=s)
		return query(ls[rr],l,r,k,vl,vm);
	else
		return query(rs[rr],l,r,k-s,vm+1,vr);
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%d%d%d%d",&n,&w,&q,&type);
	int x,i;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=n;i>=1;i--)
	{
		x=a[i];
		st[x].insert(i);
		int ed2=n;
		if(st[x].size()>=w+1)
		{
			int ed=n;
			if(st[x].size()>=w+2)
			{
				set<int>::iterator p=st[x].end();
				p--;
				ed=*p-1;
				st[x].erase(p);
			}
			set<int>::iterator p=st[x].end();
			p--;
			c[++cnt]=change(i,*p,ed,x,-w);
			ed2=*p-1;
		}
		c[++cnt]=change(i,i,ed2,x,1);
	}
	sort(c+1,c+cnt+1,cmp);
	int crt=build(1,cnt,0,n);
	int l,r,k;
	int last=0;
	for(i=1;i<=q;i++)
	{
		scanf("%d%d%d",&l,&r,&k);
		if(type)
		{
			l^=last;
			r^=last;
			k^=last;
		}
		last=query(crt,l,r,k,0,n);
		printf("%d
",last);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8513541.html