(Codeforce484E) Sign on Fence (beständiger Segmentbaum + Halbierung)

Themenlink : Problem - E - Codeforces

Die Bedeutung der Frage: Am Anfang gebe ich Ihnen n Zahlen und dann m Fragen.Das Abfrageformat ist l, r, w, was den Mindestwert der aufeinanderfolgenden w Zahlen zwischen der l-ten Zahl und der darstellt r Zahl Was ist der maximale Wert

Die Bedeutung der Frage ist etwas verwirrend, denn jedes Mal, wenn wir eine fortlaufende w-Zahl wählen, gibt es einen Mindestwert. Da die Anzahl der Auswahlmöglichkeiten unterschiedlich ist, ist auch der Mindestwert unterschiedlich. Was wir benötigen, ist der Höchstwert des Mindestwerts unter all unseren Entscheidungen.

Analyse: Diese Frage hat noch einen gewissen Denkgehalt.Zunächst können wir feststellen, dass die Antwort eine der n gegebenen Zahlen sein muss, also können wir die Höhe in zwei Teile teilen, da wir aber im Intervall rechnen müssen l bis r Es gibt keine Möglichkeit, die Kontinuitätsbedingung direkt zu lösen. Das Auftreten von Kontinuität im Liniensegmentbaum hängt offensichtlich mit der Intervallverschmelzung zusammen. Ich werde hier ein Beispiel für die Liniensegmentbaum-Intervallverschmelzung vorstellen: LCIS (Liniensegmentbaum + Intervallzusammenführung) _AC__dreams Blog-CSDN-Blog

Wir können zuerst die ursprünglich gegebenen Zahlen nach der Höhe von hoch nach niedrig sortieren und dann den Vorsitzendenbaum erstellen , dann sind wir äquivalent zur halbierenden Liniensegmentbaumversion, wenn wir die Höhe teilen ( je niedriger die Liniensegmentbaumversion, die größer der darin enthaltene Wert). ), Erklären Sie zunächst die Zahlen im Streckenbaum: Die Elemente in unserer i-ten Version des Streckenbaums sind alle größer oder gleich h[i] (nach dem Sortieren), wenn h[i ] die j-te Zahl ist, markieren wir die j-Position als 1 in der i-ten Version (relativ zum Liniensegmentbaum der i-1-ten Version) , sodass alle mit 1 markierten Positionen im Liniensegmentbaum unserer i-ten Version The enthalten Werte sind alle größer oder gleich h[i] (die Zahl, die den durch die vorherige Version markierten Wert enthält, ist größer als h[i]), sodass wir beurteilen, ob h[i] die Bedingung zum Zeitpunkt erfüllt Dichotomie, die direkt dem Linienabschnitt der i-ten Version entspricht Es genügt, die Anzahl der aufeinanderfolgenden Einsen im Intervall von l bis r am Baum abzufragen Die Umsetzung an dieser Stelle ist ganz wunderbar.

Im Segmentbaum werden insgesamt drei längenbezogene Größen erfasst:

lenl[id] gibt die Anzahl aufeinanderfolgender Einsen im Intervall mit der Nummer id an, beginnend am linken Endpunkt 
. lenr[id] gibt die Anzahl aufeinanderfolgender Einsen im Intervall mit der Nummer id an, das am rechten Endpunkt
endet. lenmx[id] gibt die Anzahl aufeinanderfolgender Einsen an im intervall nummeriert id 1 maximale länge

Die Schwierigkeit beim Zusammenführen von Liniensegmentbaumintervallen liegt im Schreiben der Pushup-Funktion. Lassen Sie uns über das Schreiben der Pushup-Funktion sprechen:

Die Funktion von Pushup ist äquivalent dazu, dass wir die Werte von lenl, lenr und lenmx in den linken und rechten Teilintervallen der Intervall-ID kennen. Wir verwenden diese Werte der linken und rechten Teilintervalle, um die Werte zu aktualisieren entsprechend diesen Variablen im aktuellen Intervall.

Schauen wir uns zuerst lenmx an. Es gibt drei Möglichkeiten. Eine ist lenmx gleich dem linken Teilintervall und die andere ist lenmx gleich dem rechten Teilintervall. Die letzte Möglichkeit besteht darin, zwei Intervalle zu überspannen, also das lenr des linken Teilintervall und Summe von lenl des rechten Teilintervalls

Was lenl betrifft, so muss diese Stelle klassifiziert und diskutiert werden: Wenn lenl des linken Teilintervalls gleich der Länge des linken Teilintervalls ist, dann ist lenl des aktuellen Intervalls gleich der Länge des linken Teilintervalls + lenl von das richtige Teilintervall

Das Lenr-Aktualisierungsverfahren ist ähnlich.Wenn der Lenr des rechten Teilintervalls gleich der Länge des rechten Teilintervalls ist, dann ist der Lenr des aktuellen Intervalls gleich der Länge des rechten Teilintervalls + dem Lenr des linken Teilintervalls

Ein weiterer zu beachtender Punkt ist bei der Durchführung von Intervallabfragen:

Wenn das durchlaufene aktuelle Intervall innerhalb des Zielintervalls liegt, können wir direkt den Maximalwert des aktuellen Intervalls zurückgeben, aber wenn das Zielintervall das linke und rechte Teilintervall des aktuellen Intervalls umfasst, können wir lenr[ln nicht direkt zurückgeben [id]]+lenl [rn[id]] Dies liegt daran , dass die darin enthaltene fortlaufende 1 das Zielintervall überschreiten kann , also müssen wir diese beiden Werte einer bestimmten Verarbeitung unterziehen. Wenn das aktuelle Intervall [l, r] ist, dann das aktuelle Intervall. Das linke Subintervall ist [l, mid] und das rechte Subintervall ist [mid+1, r], dann sollten wir die maximale Länge aufeinanderfolgender Einsen von [L, mid] und [mid] abfragen +1, R], mit anderen Worten: Die Länge unserer lenr[ln[id]] kann die Länge des Intervalls von [L,mid] nicht überschreiten, und ebenso kann die Länge von lenl[rn[id]] nicht die Länge überschreiten Länge des Intervalls von [mid+1,R], müssen wir nur den Mindestwert dieser beiden Werte nehmen .

Mit diesen Anweisungen ist der Code einfacher zu schreiben

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

Ich denke du magst

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