线段树分治总结

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

类型一

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

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

可以发现,这是一个版本树,遍历一下,就变成序列上的了。而且也是单点询问。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值