【Codechef】【Gangsters of Treeland】Lct 线段树

Problem code: MONOPLOY

给定一棵n个点的树,1号节点为根。初始时每一个点都被染成了一种不同的颜色。如果一条边的两个端点颜色不同,则其费用为1,否则费用为0。
有q次操作,操作有下面两种:
将从点u到根的路径上的所有点染成一种新的颜色。
询问点u子树中所有点走到根的费用的平均数。
n, q <= 100000.

可以发现修改操作就是Lct里的Access。然后就发现只有在虚边与实边转换时才影响答案。就转化成了线段树在Dfs序上的区间修改和区间查询。


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define RepE(i, x) for (int i = pos[x]; i; i = g[i].nex)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define u t[x]
#define v t[y]
#define lc u.ch[0]
#define rc u.ch[1]
#define Lc t[lc]
#define Rc t[rc]
#define tp u.par
#define Tp t[tp]
using namespace std;
typedef long long LL;
const int N = 200005;
struct edge { int y, nex; } g[N*2];
struct arr { int ch[2], par; void Clr() { ch[0] = ch[1] = par = 0; } } t[N];
int n, m, pos[N], sz, in[N], out[N], dz, w[N], ql, qr, qw, T, num[N];
LL lz[N*8], L[N*8], seg[N*8];
void Init(int x, int y) { g[++ sz] = (edge) { y, pos[x] }, pos[x] = sz; }
void Dfs(int x, int ft, int dep) {
	tp = ft, in[x] = ++ dz, w[dz] = dep, num[x] = 1;
	RepE(i, x) {
		int y = g[i].y;
		if (y != ft) Dfs(y, x, dep + 1), num[x] += num[y];
	}
	out[x] = dz;
}
bool d(int x) { return Tp.ch[1] == x; }
bool Sch(int x, int &y) { return (y = tp) && (v.ch[0] == x || v.ch[1] == x); }
void Sc(int x, int y, bool f) { u.ch[f] = y, v.par = x; }
void Rot(int x) {
	int y = tp, z, f = d(x);
	if (Sch(y, z)) Sc(z, x, d(y));
	else tp = z;
	Sc(y, u.ch[!f], f), Sc(x, y, !f);
}
void Splay(int x) {
	int y, z;
	while (Sch(x, y)) {
		if (!Sch(y, z)) Rot(x);
		else (d(x) == d(y)) ? (Rot(y), Rot(x)) : (Rot(x), Rot(x));
	}
}
void PD(int x) { if (lz[x]) seg[x+x] += lz[x] * L[x+x], seg[x+x+1] += lz[x] * L[x+x+1], lz[x+x] += lz[x], lz[x+x+1] += lz[x], lz[x] = 0; }
void Modify(int x, int l, int r) {
	PD(x);
	if (l > qr || r < ql) return ;
	if (l >= ql && r <= qr) { seg[x] += (r - l + 1) * qw, lz[x] += qw, PD(x); return ; }
	int mid = (l + r) >> 1;
	Modify(x+x, l, mid), Modify(x+x+1, mid+1, r);
	seg[x] = seg[x+x] + seg[x+x+1];
}
int Find(int x) { for ( Splay(x); lc; x = lc) ; return x; }
void Access(int x) {
	int y = 0;
	for (; x; x = tp) {
		Splay(x);
		if (rc != y) {
			int z = rc, qy;
			if (y) qy = Find(y);
			rc = y;
			if (z) z = Find(z), ql = in[z], qr = out[z], qw = 1, Modify(1, 1, n);
			if (y) y = qy, ql = in[y], qr = out[y], qw = -1, Modify(1, 1, n);
		} y = x;
	}
}
LL Qry(int x, int l, int r) {
	PD(x);
	if (ql <= l && r <= qr) return seg[x];
	if (ql > r || qr < l) return 0;
	int mid = (l + r) >> 1;
	return Qry(x+x, l, mid) + Qry(x+x+1, mid+1, r);
}
void Build(int x, int l, int r) {
	L[x] = r - l + 1;
	if (l == r) { seg[x] = w[l]; return ; }
	int mid = (l + r) >> 1;
	Build(x+x, l, mid), Build(x+x+1, mid+1, r);
	seg[x] = seg[x+x] + seg[x+x+1];
}
int main()
{
	scanf ("%d", &T);
	while (T --) {
		scanf ("%d", &n);
		sz = dz = 0;
		memset (pos, 0, sizeof(pos));
		memset(lz, 0, sizeof(lz));
		memset(seg, 0, sizeof(seg));
		Rep(i, 1, n-1) {
			int x, y;
			scanf ("%d%d", &x, &y), x ++, y ++;
			Init(x, y), Init(y, x);
		}
		Rep(i, 1, n) t[i].Clr();
		Dfs(1, 0, 0), Build(1, 1, n);
		scanf ("%d", &m);
		Rep(i, 1, m) {
			char ty; int x;
			scanf (" %c %d", &ty, &x), x ++;
			if (ty == 'q') ql = in[x], qr = out[x], printf("%.8lf\n", Qry(1, 1, n) / double(num[x]));
			else Access(x);
		}
	}

    return 0;
}


可以发现修改操作就是Lct里的Access。然后就发现只有在虚边与实边转换时才影响答案。就转化成了线段树在Dfs序上的区间修改和区间查询。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值