線段樹分治總結



首先,要求可以離線
線段樹分治有兩種。

類型一

操作基於區間,單點詢問。

有時,進行的一種操作可以快速完成,但是,要實現這種操作的逆操作較難。
因為,通常情況下,需要實現的逆操作都是很久以前執行的。
但是,如果只撤銷上次操作,就會簡單得多。
比如,維護一些連通性,或直徑,線性基等問題。
這類問題加邊很好做,但刪邊很難實現。
我們可以掃一遍操作,得到每個操作的有效區間。
然后,將每個添加操作的有效區間按在線段樹上,然后遍歷這顆線段樹同時處理標記即可。
從某種角度,可以理解為標記永久化。
這樣,就將撤銷任意一次變為只撤銷上一次。(還是要撤銷)
要求:用於維護的數據結構支持撤銷上一操作,復雜度不能均攤(因為要撤銷)
時間復雜度:比正常多一個log。

例題1:八縱八橫

題目鏈接:[HAOI2017]八縱八橫

線段樹分治&線性基 模板題。

給一棵樹,支持加邊,刪邊,修改邊權,並詢問最大異或和的環。
類似xor和路徑,詢問結果就是所有環的最大異或和,使用線性基。
修改可以看做刪除+插入。由於線性基不支持刪除,所以使用線段樹分治。
可以用並查集維護樹。

代碼:

#include <stdio.h> 
#include <bitset> 
#include <string.h> 
#include <vector> 
using namespace std;
int fr[503],ne[1003],v[1003],w[1003],bs = 0,len,ff[503];
bool bk[1003],ca[1003];
bitset < 1005 > bi[2003],jl[503],ji[1003],ans[1003];
void addb(int a, int b, int c) {
	v[bs] = b;
	w[bs] = c;
	ne[bs] = fr[a];
	fr[a] = bs++;
}
void dfs1(int u, int f) {
	for (int i = fr[u]; i != -1; i = ne[i]) {
		if (v[i] == f) continue;
		jl[v[i]] = jl[u] ^ bi[w[i]];
		dfs1(v[i], u);
	}
}
int getv(int x) {
	if (x == ff[x]) return x;
	ff[x] = getv(ff[x]);
	return ff[x];
}
bool merge(int x, int y) {
	x = getv(x);
	y = getv(y);
	if (x == y) return false;
	ff[x] = y;
	return true;
}
void fuz(bitset < 1005 > &x, char zf[1005]) {
	x = 0;
	int n = strlen(zf);
	if (n > len) len = n;
	for (int i = 0; i < n; i++) {
		if (zf[i] == '1') x[n - 1 - i] = 1;
	}
}
void getans(bitset < 1005 > &x) {
	x = 0;
	for (int i = len - 1; i >= 0; i--) {
		if (x[i] == 0 && bk[i]) x ^= ji[i];
	}
}
int st[1005],tp = 0;
void insert(bitset < 1005 > x) {
	for (int i = len - 1; i >= 0; i--) {
		if (x[i]) {
			if (!bk[i]) {
				bk[i] = true;
				ji[i] = x;
				st[tp++] = i;
				break;
			} else x ^= ji[i];
		}
	}
}
struct SJd {
	int x,y,z;
	SJd() {}
	SJd(int X, int Y, int Z) {
		x = X;y = Y;z = Z;
	}
};
vector < SJd > ve[8005];
void xiugai(int i, int l, int r, int L, int R, SJd x) {
	if (R <= l || r <= L) return;
	if (L <= l && r <= R) {
		ve[i].push_back(x);
		return;
	}
	int m = (l + r) >> 1;
	xiugai(i << 1, l, m, L, R, x);
	xiugai((i << 1) | 1, m, r, L, R, x);
}
int wz[2003];
void dfs3(int i, int l, int r) {
	int la = tp;
	for (int j = 0; j < ve[i].size(); j++) insert(bi[ve[i][j].z] ^ jl[ve[i][j].x] ^ jl[ve[i][j].y]);
	if (l + 1 == r) {
		if (wz[l] != -1) getans(ans[wz[l]]);
	} else {
		int m = (l + r) >> 1;
		dfs3(i << 1, l, m);
		dfs3((i << 1) | 1, m, r);
	}
	for (int i = la; i < tp; i++) bk[st[i]] = false;
	tp = la;
}
char zf[1003],ch[20];
int tx[1003],ty[1003],la[1003],tm[1003];
int ll[2003],rr[2003],X[2003],Y[2003],Z[2003];
SJd xg[2003];
int main() {
	int n,m,q;
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; i++) {
		fr[i] = -1;ff[i] = i;
	}
	for (int i = 0; i < m; i++) {
		int x,y;
		scanf("%d%d%s", &x, &y, zf);
		fuz(bi[i], zf);
		if (merge(x, y)) {
			addb(x, y, i);addb(y, x, i);
		} else tx[i] = x,
		ty[i] = y;
	}
	dfs1(1, 0);
	int ss = 0,ks = 0,xs = 0;
	for (int i = 1; i <= q; i++) {
		scanf("%s", ch);
		if (ch[0] == 'A') {
			int x,y;
			scanf("%d%d%s", &x, &y, zf);
			ks += 1;ss += 1;
			wz[ss] = i;
			fuz(bi[m + i], zf);
			la[ks] = ss;tm[ks] = i;
			X[ks] = x;Y[ks] = y;
		} else if (ch[0] == 'C' && ch[1] == 'a') {
			int k;
			scanf("%d", &k);
			ss += 1;wz[ss] = i;
			xg[xs] = SJd(X[k], Y[k], m + tm[k]);
			ll[xs] = la[k];rr[xs] = ss;
			xs += 1;
			ca[k] = true;
		} else {
			int k;
			scanf("%d%s", &k, zf);
			fuz(bi[m + i], zf);
			ss += 1;wz[ss] = -1;
			xg[xs] = SJd(X[k], Y[k], m + tm[k]);
			ll[xs] = la[k];rr[xs] = ss;
			xs += 1;wz[ss] = i;
			tm[k] = i;la[k] = ss;
		}
	}
	for (int i = 0; i < m; i++) {
		if (tx[i]) insert(bi[i] ^ jl[tx[i]] ^ jl[ty[i]]);
	}
	tp = 0;
	for (int k = 1; k <= ks; k++) {
		if (ca[k]) continue;
		xg[xs] = SJd(X[k], Y[k], m + tm[k]);
		ll[xs] = la[k];
		rr[xs] = ss + 1;
		xs += 1;
	}
	for (int i = 0; i < xs; i++) xiugai(1, 0, ss + 1, ll[i], rr[i], xg[i]);
	dfs3(1, 0, ss + 1);
	for (int i = 0; i <= q; i++) {
		bool zz = false;
		for (int j = len - 1; j >= 0; j--) {
			if (ans[i][j] == 1) {
				printf("1");
				zz = true;
			} else if (zz) printf("0");
		}
		printf("\n");
	}
	return 0;
}

例題2:時空旅行

題目鏈接:[CTSC2016]時空旅行

題意:
在一棵樹上,每個節點代表一個集合,一些元素存在這個集合之中,
每個節點上的集合,是由父親的先復制下來,然后添加或刪除1個元素,成為一個新的集合。
每個元素有\((x,y,z,c)\)四個值,\((y,z)\)沒用,就是兩個\((x,c)\)
每次給出樹上一個點,以及一個X,要求出這個節點所有元素的\(min((X−x_i)^2+C_i)\)
要求復雜度\(O(nlogn)\)

首先,看到\(min((X−x_i)^2+C_i)\),很自然想到斜率優化。
\(y_i=x_i^2+C_i,y_i=x_i,k=2X,b=y-kx,ans=b+X^2\)
那么,相當於,每個節點的凸包,是由父親的先復制下來,然后添加或刪除1個點,成為一個新的凸包。

可以發現,這是一個版本樹,遍歷一下,就變成序列上的了。而且也是單點詢問。
那么,我們考慮使用上題的方法。
會發現兩個問題:
1、凸包添加刪除是均攤的。
2、無法保證x遞增。
但是,我們可以按x遞增的順序在線段樹上添加凸包,使每個節點的x遞增。
這時,由於各個節點的凸包互不影響(不像上題的線性基是互相影響的),我們可以對於線段樹上的葉子節點暴力找所有祖先進行計算。
按照斜率遞減計算,即可用單調棧維護凸包,時空復雜度\(O(nlogn)\),卡卡空間就能過。

代碼:

#include <stdio.h>
#include <vector>
#include <stdlib.h>
#define ll long long
#define inf 99999999999999999
void read(ll &x)
{
	int f=1;x=0;
	char s=getchar();
	while(s<'0'||s>'9')
	{
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	x*=f;
}
void read(int &x)
{
	int f=1;x=0;
	char s=getchar();
	while(s<'0'||s>'9')
	{
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	x*=f;
}
using namespace std;
struct point
{
	int x;
	ll y;
	point(){}
	point(int X,ll Y)
	{
		x=X;y=Y;
	}
};
point pt[500010];
int cmp(int ia,int ib,int ic,int id)
{
	point a=pt[ia],b=pt[ib],c=pt[ic],d=pt[id];
	if(1ll*(b.y-a.y)*(d.x-c.x)<1ll*(d.y-c.y)*(b.x-a.x))
		return -1;
	else if(1ll*(b.y-a.y)*(d.x-c.x)>1ll*(d.y-c.y)*(b.x-a.x))
		return 1;
	else
		return 0;
}
int cmp(int ia,int ib,int k)
{
	point a=pt[ia],b=pt[ib];
	if(b.y-a.y<1ll*k*(b.x-a.x))
		return -1;
	else if(b.y-a.y>1ll*k*(b.x-a.x))
		return 1;
	else
		return 0;
}
#define tubao vector<int>
void insert(tubao &tb,int a)
{
	int sl=tb.size();
	if(sl>0&&pt[tb[sl-1]].x==pt[a].x)
	{
		if(pt[a].y<pt[tb[sl-1]].y)
		{
			tb.pop_back();
			sl-=1;
		}
		else return;
	}
	while(sl>=2&&cmp(tb[sl-2],tb[sl-1],tb[sl-1],a)!=-1)
	{
		tb.pop_back();
		sl-=1;
	}
	tb.push_back(a);
}
ll findb(tubao &tb,int k)
{
	int sl=tb.size();
	if(sl==0)return inf;
	while(sl>=2&&cmp(tb[sl-2],tb[sl-1],k)!=-1)
	{
		tb.pop_back();
		sl-=1;
	}
	return pt[tb[sl-1]].y-1ll*k*pt[tb[sl-1]].x;
}
ll find(tubao &tb,int X)
{
	return findb(tb,X*2)+1ll*X*X;
}
tubao ve[2097160];int wz[1000010];
ll findans(int u,int X)
{
	ll zx=inf;
	while(u>0)
	{
		ll t=find(ve[u],X);
		if(t<zx)zx=t;
		u/=2;
	}
	return zx;
}
void jianshu(int i,int l,int r)
{
	if(l+1==r)
	{
		wz[l]=i;
		return;
	}
	int m=(l+r)>>1;
	jianshu(i<<1,l,m);
	jianshu((i<<1)|1,m,r);
}
void insert(int i,int l,int r,int L,int R,int x)
{
	if(r<=L||R<=l)
		return;
	if(L<=l&&r<=R)
	{
		insert(ve[i],x);
		return;
	}
	int m=(l+r)>>1;
	insert(i<<1,l,m,L,R,x);
	insert((i<<1)|1,m,r,L,R,x);
}
int fr[500010],ne[500010],v[500010],bs=0;
void addb(int a,int b)
{
	v[bs]=b;
	ne[bs]=fr[a];
	fr[a]=bs++;
}
int lx[500010],id[500010],zx[500010];
int wl[500010],wr[500010],la[500010],tm=0;
void dfs(int u)
{
	wl[u]=tm++;
	for(int i=fr[u];i!=-1;i=ne[i])
		dfs(v[i]);
	wr[u]=tm++;
}
struct SCz
{
	int lx,i;
	SCz(){}
	SCz(int Lx,int I)
	{
		lx=Lx;i=I;
	}
};
vector<SCz> cz[1000010];
struct SPx
{
	int x,l,r;
	SPx(){}
	SPx(int X,int L,int R)
	{
		x=X;l=L;r=R;
	}
};
SPx px[1000010];
int cmp2(const void*a,const void*b)
{
	return zx[((SPx*)a)->x]-zx[((SPx*)b)->x];
}
struct SXw
{
	int x,s,i;
	SXw(){}
	SXw(int X,int S,int I)
	{
		x=X;s=S;i=I;
	}
};
SXw xw[500010];
int cmp3(const void*a,const void*b)
{
	return ((SXw*)b)->x-((SXw*)a)->x;
}
ll ans[500010];
int main()
{
	int n,m,s=0;ll c0;
	scanf("%d%d%lld",&n,&m,&c0);
	for(int i=0;i<n;i++)
		fr[i]=-1;
	for(int i=1;i<n;i++)
	{
		int y,z,a;
		read(lx[i]);
		if(lx[i]==0)
		{
			int x;ll c;
			read(a);read(id[i]);read(x);read(y);read(z);read(c);
			pt[id[i]]=point(x,1ll*x*x+c);
			zx[id[i]]=x;
		}
		else
		{
			read(a);
			read(id[i]);
		}
		addb(a,i);
	}
	dfs(0);
	for(int i=1;i<n;i++)
	{
		if(lx[i]==0)cz[wr[i]].push_back(SCz(1,id[i]));
		else cz[wl[i]].push_back(SCz(1,id[i]));
	}
	for(int i=1;i<n;i++)
	{
		if(lx[i]==0)cz[wl[i]].push_back(SCz(0,id[i]));
		else cz[wr[i]].push_back(SCz(0,id[i]));
	}
	pt[0]=point(0,c0);jianshu(1,0,tm);
	insert(1,0,tm,0,tm,0);
	for(int i=0;i<=tm;i++)
	{
		for(int j=0;j<cz[i].size();j++)
		{
			int lx=cz[i][j].lx,x=cz[i][j].i;
			if(lx==0)
				la[x]=i;
			else
				px[s++]=SPx(x,la[x],i);
		}
	}
	qsort(px,s,sizeof(SPx),cmp2);
	for(int i=0;i<s;i++)
		insert(1,0,tm,px[i].l,px[i].r,px[i].x);
	for(int i=0;i<m;i++)
	{
		int s,x;
		read(s);read(x);
		xw[i]=SXw(x,s,i);
	}
	qsort(xw,m,sizeof(SXw),cmp3);
	for(int i=0;i<m;i++)
		ans[xw[i].i]=findans(wz[wl[xw[i].s]],xw[i].x);
	for(int i=0;i<m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

類型二

單點操作,區間詢問。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM