(Codeforce484E) Sign on Fence (arbre de segment persistant + bissection)

Lien vers le sujet : Problème - E - Codeforces

Le sens de la question : Au début, je vais vous donner n nombres, puis vous donner m requêtes. Le format de la requête est l, r, w, qui représente la valeur minimale des w nombres consécutifs entre le lième nombre et le nombre r. quelle est la valeur maximale

Le sens de la question est un peu déroutant, car chaque fois que nous choisissons un nombre w continu, il y aura une valeur minimale. Parce que le nombre de choix est différent, la valeur minimale sera également différente. Ce dont nous avons besoin est la valeur maximale de la valeur minimale parmi tous nos choix.

Analyse : Cette question a encore un certain contenu de réflexion. Tout d'abord, nous pouvons trouver que la réponse doit être l'un des n nombres donnés, nous pouvons donc diviser la hauteur en deux parties, mais comme nous devons effectuer dans l'intervalle l à r Il n'y a aucun moyen de résoudre directement la condition de continuité. L'apparition de la continuité dans l'arbre de segments de ligne est évidemment liée à la fusion d'intervalles. Je vais partager ici un exemple de fusion d'intervalles d'arbre de segments de ligne : LCIS (arbre de segments de ligne + fusion d'intervalle) _AC__dream's blog-CSDN blog

Nous pouvons d'abord trier les nombres donnés à l'origine en fonction de la hauteur de haut en bas, puis créer l'arbre du président , puis nous sommes équivalents à la version d'arbre de segment de ligne de bissection lorsque nous divisons la hauteur ( plus la version d'arbre de segment de ligne est basse, le plus la valeur qu'il contient). ), expliquez d'abord les nombres dans l'arborescence des segments de ligne. Les éléments de notre ième version de l'arborescence des segments de ligne sont tous supérieurs ou égaux à h[i] (après tri), si h[i ] est le jième nombre, nous marquons la j position comme 1 sur la ième version (par rapport à l'arborescence de segments de ligne de la i-1ème version) , de sorte que toutes les positions marquées 1 dans l'arborescence de segments de ligne de notre ième version contiennent The les valeurs sont toutes supérieures ou égales à h[i] (le nombre contenant la valeur marquée par la version précédente est supérieur à h[i]), de sorte que l'on juge si h[i] satisfait la condition au moment de dichotomie, qui équivaut directement au segment de ligne de la ième version Il suffit d'interroger le nombre de 1 consécutifs dans l'intervalle de l à r sur l'arbre.La conversion à cet endroit est tout à fait merveilleuse.

Au total, trois variables liées à la longueur sont enregistrées dans l'arborescence des segments :

lenl[id] indique le nombre de 1 consécutifs dans l'intervalle numéroté id commençant à l'extrémité gauche 
lenr[id] indique le nombre de 1 consécutifs dans l'intervalle numéroté id se terminant à l'extrémité droite
lenmx[id] indique le nombre de 1 consécutifs dans l'intervalle numéroté id 1 longueur maximale

La difficulté de la fusion d'intervalles d'arbres de segments de droite réside dans l'écriture de la fonction pushup. Parlons de l'écriture de la fonction pushup :

La fonction de pushup équivaut à ce que nous connaissions les valeurs de lenl, lenr et lenmx dans les sous-intervalles gauche et droit de l'intervalle id. Nous utilisons ces valeurs des sous-intervalles gauche et droit pour mettre à jour les valeurs correspondant à ces variables dans l'intervalle courant.

Examinons d'abord lenmx. Il existe trois possibilités. L'une est lenmx égale au sous-intervalle gauche, et l'autre est lenmx égale au sous-intervalle droit. La dernière possibilité consiste à couvrir deux intervalles, c'est-à-dire le lenr de la gauche sous-intervalle et somme de lenl du sous-intervalle droit

Comme pour lenl, cet endroit doit être classé et discuté. Si le lenl du sous-intervalle de gauche est égal à la longueur du sous-intervalle de gauche, alors le lenl de l'intervalle courant est égal à la longueur du sous-intervalle de gauche + le lenl de le bon sous-intervalle

La méthode de mise à jour lenr est similaire. Si le lenr du sous-intervalle droit est égal à la longueur du sous-intervalle droit, alors le lenr de l'intervalle actuel est égal à la longueur du sous-intervalle droit + le lenr du sous-intervalle gauche

Un autre point à noter est lors de l'exécution de requêtes d'intervalle :

Si l'intervalle courant traversé se trouve dans l'intervalle cible, nous pouvons renvoyer directement la valeur maximale de l'intervalle courant, mais si l'intervalle cible couvre les sous-intervalles gauche et droit de l'intervalle courant, nous ne pouvons pas renvoyer directement lenr[ln [id]]+lenl [rn[id]] C'est parce que le 1 continu contenu dans this peut dépasser l'intervalle cible , nous devons donc effectuer certains traitements sur ces deux valeurs. Si l'intervalle actuel est [l, r], alors l'intervalle courant Le sous-intervalle de gauche est [l, mid], et le sous-intervalle de droite est [mid+1, r], alors nous devrions interroger la longueur maximale des 1 consécutifs de [L, mid] et [mid +1, R], en d'autres termes, la longueur de notre lenr[ln[id]] ne peut pas dépasser la longueur de l'intervalle de [L,mid], et de même la longueur de lenl[rn[id]] ne peut pas dépasser la longueur de l'intervalle de [mid+1,R], il suffit de prendre la valeur minimale de ces deux valeurs .

Avec ces instructions, le code est plus facile à écrire

#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;
}

Je suppose que tu aimes

Origine blog.csdn.net/AC__dream/article/details/123843372
conseillé
Classement