zoukankan      html  css  js  c++  java
  • 线段树分治总结


    首先,要求可以离线
    线段树分治有两种。

    类型一

    操作基于区间,单点询问。

    有时,进行的一种操作可以快速完成,但是,要实现这种操作的逆操作较难。
    因为,通常情况下,需要实现的逆操作都是很久以前执行的。
    但是,如果只撤销上次操作,就会简单得多。
    比如,维护一些连通性,或直径,线性基等问题。
    这类问题加边很好做,但删边很难实现。
    我们可以扫一遍操作,得到每个操作的有效区间。
    然后,将每个添加操作的有效区间按在线段树上,然后遍历这颗线段树同时处理标记即可。
    从某种角度,可以理解为标记永久化。
    这样,就将撤销任意一次变为只撤销上一次。(还是要撤销)
    要求:用于维护的数据结构支持撤销上一操作,复杂度不能均摊(因为要撤销)
    时间复杂度:比正常多一个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("
    ");
    	}
    	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
    ",ans[i]);
    	return 0;
    }
    

    类型二

    单点操作,区间询问。

  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1079:计算分数加减表达式的值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1077:统计满足条件的4位数
    晕牛【拓扑排序】【BFS】
    过路费【Floyd】
    电视游戏问题【DP】【背包】
    音乐节拍【模拟】
    密码【高精】
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11614311.html
Copyright © 2011-2022 走看看