(Codeforce484E) Sign on Fence (persistent segment tree + bisection)

Topic link: Problem - E - Codeforces

The meaning of the question: At the beginning, I will give you n numbers, and then give you m queries. The query format is l, r, w, which represents the minimum value of the consecutive w numbers between the lth number and the r number. what is the maximum value

The meaning of the question is a bit confusing, because every time we choose a continuous w number, there will be a minimum value. Because the number of choices is different, the minimum value will also be different. What we require is the maximum value of the minimum value among all our choices.

Analysis: This question still has a certain thinking content. First of all, we can find that the answer must be one of the n numbers given, so we can divide the height into two parts, but since we have to carry out in the interval l to r There is no way to directly solve the condition of continuity. The appearance of continuity in the line segment tree is obviously related to the interval merging. I share an example of the line segment tree interval merging here: LCIS (line segment tree + interval merging) _AC__dream's blog-CSDN blog

We can first sort the originally given numbers according to the height from high to low, and then create the chairman tree , then we are equivalent to the bisection line segment tree version when we divide the height ( the lower the line segment tree version, the larger the value in it). ), first explain the numbers in the line segment tree. The elements in our ith version of the line segment tree are all higher than or equal to h[i] (after sorting), if h[i] is the jth number, we mark the j position as 1 on the ith version (relative to the line segment tree of the i-1th version) , so that all the positions marked 1 in the line segment tree of our ith version contain The values ​​are all greater than or equal to h[i] (the number containing the value marked by the previous version is greater than h[i]), so that we judge whether h[i] satisfies the condition at the time of dichotomy, which is equivalent to the line segment of the i-th version directly It is enough to query the number of consecutive 1s in the interval from l to r on the tree. The conversion in this place is quite wonderful.

A total of three length-related variables are recorded in the segment tree:

lenl[id] indicates the number of consecutive 1s in the interval numbered id starting from the left endpoint 
lenr[id] indicates the number of consecutive 1s in the interval numbered id ending at the right endpoint
lenmx[id] indicates the number of consecutive 1s in the interval numbered id 1 maximum length

The difficulty of the line segment tree interval merging lies in the writing of the pushup function. Let's talk about the writing of the pushup function:

The function of pushup is equivalent to that we know the values ​​of lenl, lenr, and lenmx in the left and right subintervals of the interval id. We use these values ​​of the left and right subintervals to update the values ​​corresponding to these variables in the current interval.

Let's take a look at lenmx first. There are three possibilities. One is lenmx equal to the left subinterval, and the other is lenmx equal to the right subinterval. The last possibility is to span two intervals, that is, the lenr of the left subinterval and sum of lenl of the right subinterval

As for lenl, this place needs to be classified and discussed. If the lenl of the left subinterval is equal to the length of the left subinterval, then the lenl of the current interval is equal to the length of the left subinterval + the lenl of the right subinterval

The lenr update method is similar. If the lenr of the right subinterval is equal to the length of the right subinterval, then the lenr of the current interval is equal to the length of the right subinterval + the lenr of the left subinterval

Another point to note is when performing interval queries:

If the current interval traversed happens to be within the target interval, we can directly return the maximum value of the current interval, but if the target interval spans the left and right sub-intervals of the current interval, we cannot directly return lenr[ln[id]]+lenl [rn[id]] This is because the continuous 1 contained in this may exceed the target interval , so we need to perform certain processing on these two values. If the current interval is [l, r], then the current interval The left sub-interval is [l, mid], and the right sub-interval is [mid+1, r], then we should query the maximum length of consecutive 1s of [L, mid] and [mid+1, R], in other words, The length of our lenr[ln[id]] cannot exceed the length of the interval of [L,mid], and similarly the length of lenl[rn[id]] cannot exceed the length of the interval of [mid+1,R], we only need to Take the minimum value of these two values .

With these instructions, the code is easier to write

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e5+10;
const int M=20; 
int ln[N*M],rn[N*M],root[N],idx;
int lenl[N*M],lenr[N*M],lenmx[N*M];
//lenl[id]表示编号为id的区间以左端点开始连续1的个数 
//lenr[id]表示编号为id的区间以右端点结束连续1的个数
//lenmx[id]表示编号为id的区间中连续1的最大长度
struct node{
	int h,id;
}p[N];
bool cmp(node a,node b)
{
	return a.h>b.h;
}
void pushup(int id,int l,int r)//区间合并 
{
	lenmx[id]=max(lenmx[ln[id]],lenmx[rn[id]]);//当前区间的最长连续1的个数分布在一个区间中 
	lenmx[id]=max(lenmx[id],lenr[ln[id]]+lenl[rn[id]]);//当前区间的最长连续1的个数分布在左右两个子区间中
	int mid=l+r>>1;
	if(lenl[ln[id]]==(mid-l+1))//当前区间左子区间以左端点开始连续1的个数等于左子区间长度,当前区间以左端点开始连续1的个数就应该计算上右子区间以左端点开始连续1的个数 
		lenl[id]=lenl[ln[id]]+lenl[rn[id]];
	else//否则当前区间以左端点开始连续1的个数就是当前区间以左子区间左端点开始连续1的个数 
		lenl[id]=lenl[ln[id]];
	if(lenr[rn[id]]==(r-mid))//当前区间右子区间以右端点结束连续1的个数等于右子区间长度,当前区间以右端点结束连续1的个数就应该计算上左子区间以右端点结束连续1的个数 
		lenr[id]=lenr[rn[id]]+lenr[ln[id]];
	else//否则当前区间以右端点结束连续1的个数就是当前区间以右子区间右端点结束连续1的个数 
		lenr[id]=lenr[rn[id]];
	return ;
}
void update_point(int pre,int id,int pos,int l,int r)
{
	ln[id]=ln[pre];rn[id]=rn[pre];lenl[id]=lenl[pre];lenr[id]=lenr[pre];lenmx[id]=lenmx[pre];
	if(l==r)
	{
		lenl[id]=lenr[id]=lenmx[id]=1;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid) ln[id]=++idx,update_point(ln[pre],ln[id],pos,l,mid);
	else rn[id]=++idx,update_point(rn[pre],rn[id],pos,mid+1,r);
	pushup(id,l,r);
}
int query_interval(int id,int L,int R,int l,int r)
{
	if(l>=L&&r<=R) return lenmx[id];
	int mid=l+r>>1;
	int ans=0;
	if(L<=mid) ans=max(ans,query_interval(ln[id],L,R,l,mid));
	if(mid+1<=R) ans=max(ans,query_interval(rn[id],L,R,mid+1,r));
	ans=max(ans,min(lenr[ln[id]],/*左子区间长度*/mid-L+1)+min(lenl[rn[id]],/*右子区间长度*/R-mid));//把区间[L,R]分成两段,但是要保证计算连续1长度时不能包含区间[L,R]之外的部分,所以应该取最小值 
	return ans;
}
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i].h),p[i].id=i;
	sort(p+1,p+n+1,cmp);//按照高度进行排序 
	for(int i=1;i<=n;i++)
	{
		root[i]=++idx;
		update_point(root[i-1],root[i],p[i].id,1,n);//低版本的线段树中的高度是较高的 
	}
	int m;
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		int l,r,len;
		scanf("%d%d%d",&l,&r,&len);
		int ll=1,rr=n;//二分线段树的版本 
		while(ll<rr)
		{
			int mid=ll+rr>>1;//低版本的线段树中的高度是较高的
			if(query_interval(root[mid],l,r,1,n)>=len) rr=mid;//所以当较高版本线段树中能够满足有连续len个1存在,应该降低线段树版本继续搜索 
			else ll=mid+1;
		}
		printf("%d\n",p[ll].h);//输出最终线段树版本对应插入的数 
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/AC__dream/article/details/123843372