[论树剖与DFS序的正确姿势]

[BZOJ 2243]染色

裸树剖

因为没有赋size[u]=1写T了T^T

这个东西有区间可并性QAQ

#include 
#include 
#include 
#include 
#define maxn 100010
using namespace std;

int n, q;

struct Node{int l, r, val, pre, suf, lazy;};
int c[maxn], dfn[maxn], dfs_clock;
struct Segment{
	Node t[maxn << 2];
	#define lc id<<1
	#define rc id<<1|1
	void pushdown(int id){
		if(t[id].lazy){
			t[lc].lazy = t[rc].lazy = t[id].lazy;
			t[lc].pre = t[lc].suf = t[rc].pre = t[rc].suf = t[id].lazy;
			t[lc].val = t[rc].val = 1;
			t[id].lazy = 0;
		}
	}
	
	void pushup(int id){
        t[id].val = t[lc].val + t[rc].val - (t[lc].suf == t[rc].pre);
		t[id].pre = t[lc].pre;
		t[id].suf = t[rc].suf;
	}
	
	void build(int id, int l, int r){
		t[id].l = l, t[id].r = r;
		if(l == r){t[id].val = 1;t[id].pre = t[id].suf = c[dfn[l]];return;}
		int mid = l + r >> 1;
		build(lc, l, mid);
		build(rc, mid+1, r);
		pushup(id);
	}
	
	void update(int id, int l, int r, int val){
		if(t[id].l == l && t[id].r == r){
			t[id].val = 1;
			t[id].pre = t[id].suf = t[id].lazy = val;
			return;
		}
		pushdown(id);
		int mid = t[id].l + t[id].r >> 1;
		if(r <= mid)update(lc, l, r, val);
		else if(l > mid)update(rc, l, r, val);
		else{
			update(lc, l, mid, val);
			update(rc, mid+1, r, val);
		}
		pushup(id);
	}
	
	Node ask(int id, int l, int r){
        if(t[id].l == l && t[id].r == r)
			return t[id];
		pushdown(id);
		int mid = t[id].l + t[id].r >> 1;
		if(r <= mid)return ask(lc, l, r);
		if(l > mid)return ask(rc, l, r);
		Node ret1 = ask(lc, l, mid), ret2 = ask(rc, mid+1, r), ret;
		ret.val = ret1.val + ret2.val - (ret1.suf == ret2.pre);
		ret.pre = ret1.pre, ret.suf = ret2.suf;
		return ret;
	}
}T;

struct Edge{int to, next;}edge[maxn << 1];
int h[maxn], cnt;
void add(int u, int v){
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	h[u] = cnt;
}

const int root = 1;
int size[maxn], top[maxn], pos[maxn], son[maxn], fa[maxn], dep[maxn];

void dfs1(int u){
	dep[u] = dep[fa[u]] + 1;
	size[u] = 1;
	for(int i = h[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == fa[u])continue;
		fa[v] = u;
		dfs1(v);
		size[u] += size[v];
		if(size[v] > size[son[u]])
		    son[u] = v;
	}
}

void dfs2(int u, int tp){
	top[u] = tp;
	pos[u] = ++ dfs_clock;
	dfn[dfs_clock] = u;
	if(son[u])dfs2(son[u], tp);
	for(int i = h[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == fa[u] || v == son[u])continue;
		dfs2(v, v);
	}
}

void Modify(int u, int v, int c){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])
			swap(u, v);
		T.update(1, pos[top[u]], pos[u], c);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v])swap(u, v);
	T.update(1, pos[v], pos[u], c);
}

Node ret;
int solve(int u, int v){
	int last1 = 0, last2 = 0, ans = 0;
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])
		    swap(u, v), swap(last1, last2);
		ret = T.ask(1, pos[top[u]], pos[u]);
		ans += ret.val;
		if(last1 == ret.suf)ans --;
		last1 = ret.pre;
		u = fa[top[u]];
	}
	if(dep[u] < dep[v])swap(u, v), swap(last1, last2);
	ret = T.ask(1, pos[v], pos[u]);
	ans += ret.val;
	if(last2 == ret.pre)ans --;
	if(last1 == ret.suf)ans --;
	return ans;
}

int main(){
	scanf("%d%d", &n, &q);
	for(int i = 1; i <= n; i ++)
		scanf("%d", &c[i]);
	int u, v, d;
	for(int i = 1; i < n; i ++){
		scanf("%d%d", &u, &v);
		add(u, v), add(v, u);
	}
	dfs1(root);
	dfs2(root, root);
	T.build(1, 1, n);
	
	for(int i = 1; i <= q; i ++){
        char QAQ = getchar();
		for(; QAQ < '!'; QAQ = getchar());
		if(QAQ == 'C'){
			scanf("%d%d%d", &u, &v, &d);
			Modify(u, v, d);
		}
		else{
			scanf("%d%d", &u, &v);
			printf("%d\n", solve(u, v));
		}
	}
	
	return 0;
}





[BZOJ3083]遥远的国度

注意到树剖维护的线段树是一个dfs序

所以查询一棵子树就变成了dfs序上的一段区间

然后。。

换根嘛

就是查询的区间不同啦

一共就三种情况嘛~

No1.u=root 那全局最小值就OK啦

No2.root在u的子树外 那就是u的子树中的最小值啊~

No3.root在u的子树内

[论树剖与DFS序的正确姿势]_第1张图片

就是这样啦

我们要找到root属于哪个子树(但是千万不要找到fa[u]!)

想象一下把root拎起来,然后查询蓝色部分+u的地方的最小值!

然后就是查询除了v这棵子树的最小值啦

(话说菊花树怎么破)


然后神犇机智的回答

遥远的国度菊花树的解决方法很简单
用vector存下每个节点的儿子
之后预处理对vector排序
之后每次查询二分就可以了
%%%%%%%%%%%%


#include 
#define maxn 100010
using namespace std;
typedef long long ll;
int n, m;

struct Node{
	int l, r;
	ll lazy, min, val;
};

ll a[maxn];
int dfn[maxn], dfs_clock;

struct Segment{
	Node t[maxn << 2];
	#define lc id<<1
	#define rc id<<1|1
	void pushup(int id){
		t[id].min = min(t[lc].min, t[rc].min);
	}
	
	void pushdown(int id){
		if(t[id].lazy){
			t[lc].lazy = t[rc].lazy = t[id].lazy;
			t[lc].min = t[rc].min = t[id].lazy;
			t[lc].val = t[rc].val = t[id].lazy;
			t[id].lazy = 0;
		}
	}
	
	void build(int id, int l, int r){
		t[id].l = l, t[id].r = r;
		if(l == r){
			t[id].val = t[id].min = a[dfn[l]];
			return;
		}
		int mid = l+r >> 1;
		build(lc, l, mid);
		build(rc, mid+1, r);
		pushup(id);
	}

	void cover(int id, int l, int r, ll c){
		if(t[id].l == l && t[id].r == r){
			t[id].lazy = t[id].val = t[id].min = c;
			return;
		}
		pushdown(id);
		int mid = t[id].l + t[id].r >> 1;
		if(r <= mid)cover(lc, l, r, c);
		else if(l > mid)cover(rc, l, r, c);
		else{
			cover(lc, l, mid, c);
			cover(rc, mid+1, r, c);
		}
		pushup(id);
	}
	
	ll ask(int id, int l, int r){
		if(t[id].l == l && t[id].r == r)
		    return t[id].min;
		pushdown(id);
		int mid = t[id].l + t[id].r >> 1;
		if(r <= mid)return ask(lc, l, r);
		if(l > mid)return ask(rc, l, r);
		return min(ask(lc, l, mid), ask(rc, mid+1, r));
	}
}T;

struct Edge{int to, next;}edge[maxn << 1];
int h[maxn], cnt;
void add(int u, int v){
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	h[u] = cnt;
}

int root;

int fa[maxn], dep[maxn], size[maxn], son[maxn], top[maxn], pos[maxn];
int L[maxn], R[maxn];
void dfs1(int u){
	dep[u] = dep[fa[u]] + 1;
	size[u] = 1;
	for(int i = h[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == fa[u])continue;
		fa[v] = u;
		dfs1(v);
		size[u] += size[v];
		if(size[v] > size[son[u]])
		    son[u] = v;
	}
}

void dfs2(int u, int tp){
	L[u] = pos[u] = ++ dfs_clock;
	top[u] = tp;
	dfn[dfs_clock] = u;
	if(son[u])dfs2(son[u], tp);
	for(int i = h[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if(v == son[u] || v == fa[u])continue;
		dfs2(v, v);
	}
	R[u] = dfs_clock;
}

void Modify(int u, int v, ll c){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])
		    swap(u, v);
		T.cover(1, pos[top[u]], pos[u], c);
		u = fa[top[u]];
	}
	if(dep[u] < dep[v])swap(u, v);
	T.cover(1, pos[v], pos[u], c);
}

ll solve(int u){
	if(u == root)return T.ask(1, 1, n);
	if(pos[root] >= L[u] && pos[root] <= R[u]){
		for(int i = h[u]; i; i = edge[i].next){
			int v = edge[i].to;
			if(v == fa[u])continue;
			if(pos[root] >= L[v] && pos[root] <= R[v]){
				u = v;
				break;
			}
		}
		ll t = 0x7fffffff;
		if(L[u] - 1)t = T.ask(1, 1, L[u]-1);
		if(R[u] < n)t = min(t, T.ask(1, R[u]+1, n));
		return t;
	}
	return T.ask(1, L[u], R[u]);
}

int main(){
	scanf("%d%d", &n, &m);
	int u, v;
	ll d;
	for(int i = 1; i < n; i ++){
		scanf("%d%d", &u, &v);
		add(u, v), add(v, u);
	}
	for(int i = 1; i <= n; i ++)
	    scanf("%lld", &a[i]);
	scanf("%d", &root);
	dfs1(root);
	dfs2(root, root);
	T.build(1, 1, n);
	int opt;
	for(int i = 1; i <= m; i ++){
		scanf("%d", &opt);
		if(opt == 1)scanf("%d", &root);
		else if(opt == 2){
			scanf("%d%d%lld", &u, &v, &d);
			Modify(u, v, d);
		}
		else{
			scanf("%d", &u);
			printf("%lld\n", solve(u));
		}
	}
	return 0;
}




DFS序

也是很重要的

如上题所示

有一棵点数为N的树,以点1为根,且树点有边权。然后有M个操作,分为三种:

操作1:把某个节点x的点权增加a。

操作2:把某个节点x为根的子树中所有点的点权都增加a。

操作3:询问某个节点x到根的路径中所有点的点权和。

如果把操作 贡献化

ans = (dep[u] - (dep[x]-1)) * a

发现只需要维护一个K和一个B

然后dfs序中进栈用In表示,出栈用Out表示

注意In[x]和Out[x]都要更改T^T(估计没有人像我一样只改In吧,咦,没有人是谁?)
#include 
#define maxn 200010
using namespace std;
typedef long long ll;
struct Edge{
	int to, next;
}edge[maxn];

int h[maxn], cnt, n, m;
void add(int u, int v){
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].next = h[u];
	h[u] = cnt;
}

const int root = 1;

int a[maxn];

int In[maxn], Out[maxn], dfs_clock, fa[maxn];
int dep[maxn];
void dfs(int u){
	In[u] = ++ dfs_clock;
	dep[u] = dep[fa[u]] + 1;
	for(int i = h[u]; i; i = edge[i].next){
		if(edge[i].to == fa[u])continue;
		fa[edge[i].to] = u;
		dfs(edge[i].to);
	}
	Out[u] = dfs_clock;
}

struct BIT{
	ll t[maxn];
	#define lowbit(i) i&(~i+1)
	void update(int pos, ll val){
		if(!pos)return;
		for(int i = pos; i <= n+1; i += lowbit(i))
		    t[i] += val;
	}
	ll ask(int pos){
		if(!pos)return 0;
		ll ret = 0;
		for(int i = pos; i; i -= lowbit(i))
		    ret += t[i];
		return ret;
	}
}K, B;

void Modify(int x, ll a, int type){
	if(type){
		B.update(In[x], -a*(dep[x]-1));
		B.update(Out[x]+1, a*(dep[x]-1));
		K.update(In[x], a);
		K.update(Out[x]+1, -a);
	}
	else B.update(In[x], a), B.update(Out[x]+1, -a);
}

ll ask(int x){
	return K.ask(In[x]) * dep[x] + B.ask(In[x]);
}

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++)
	    scanf("%d", &a[i]);
	int u, v;
	for(int i = 1; i < n; i ++){
		scanf("%d%d", &u, &v);
		add(u, v), add(v, u);
	}
	
	dfs(root);
	for(int i = 1; i <= n; i ++){
		B.update(In[i], a[i]);
		B.update(Out[i]+1, -a[i]);
	}
	for(int i = 1; i <= m; i ++){
		scanf("%d", &u);
		if(u == 1){
			scanf("%d%d", &u, &v);
			Modify(u, v, 0);
		}
		else if(u == 2){
			scanf("%d%d", &u, &v);
			Modify(u, v, 1);
		}
		else{
			scanf("%d", &u);
			printf("%lld\n", ask(u));
		}
	}
	return 0;
}


你可能感兴趣的:(数据结构--线段树,树--树分治。树链剖分,BZOJ)