嘿嘿嘿嘿的 zy 树链剖分+线段树+树上带修莫队

4 篇文章 0 订阅
2 篇文章 0 订阅

题意

给一颗 n 个点的树,每一个点有一个颜色,然后维护几个操作。
操作 1:t=1 时,将 x 点的颜色修改为 y
操作 2:t=2 时,询问 x 到 y 路径上有多少个不同的颜色段
操作 3:t=3 时,询问 x 到 y 路径上的出现次数最多的颜色的出现次数

操作2是[SDOI2011]染色原题
操作3是树上带修莫队,关于如何求出出现次数最多的颜色出现次数,考虑到时莫队做法,一次只能+1或-1,所以ans也一定是+1或-1,可以用数组记下每种出现次数的总次数和,这样就可以在移动指针的时候修改ans了。
千万不要用树套树,考试时打了个线段树套动态开点线段树,空间完全开不下,只能过20分。。。

#include <bits/stdc++.h>
#define jh(x, y) (x ^= y, y ^= x, x ^= y)
#define ls (p << 1)
#define rs (p << 1 | 1)
#define li T2[p].l
#define ri T2[p].r
#define mid ((l + r) >> 1)
#define ll long long
using namespace std;
inline void read(int &x) {
	x = 0; int f = 1; char ch = getchar();
	while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + ch - 48; ch = getchar(); }
	x *= f;
}
inline void Max(int &x, int y) { if (y > x) x = y; }
inline void Min(int &x, int y) { if (y < x) x = y; }
const int N = 1e5 + 10;
struct Seg1 { int lc, rc, sum; } T1[N << 2];
struct Edge { int b, nt; } e[N << 1];
struct Query { int opt, x, y; } query[N];
int head[N], e_num;
inline void anode(int u, int v) {
	e[++e_num].b = v; e[e_num].nt = head[u]; head[u] = e_num;
}
///MO--------------------------------------------------------
int blo, cnta, cntc, tot, cur, pl = 1, pr = 0;
int ord[N << 1], ans[N], fir[N], sec[N], jp[N][21], cnt[N], out[N], res;
bool use[N];
#define bel(x) ((x - 1) / blo + 1)
struct Ask {
	int l, r, lca, tim, aim;
	bool operator < (const Ask &b) const {
		if (bel(l) != bel(b.l)) return l < b.l;
		if (bel(r) != bel(b.r)) return r < b.r;
		return tim < b.tim;
	}
} ask[N];
struct Chg { int aim, tim, val; } chg[N];
//mo----------------------------------------------------------
int n, Q, rt[N << 2];
queue<int> que;
int val[N];
int sz[N], prt[N], dep[N], son[N], bel[N], id, pos[N], h[N];
inline void dfs1(int u) {
	fir[u] = ++tot;
	ord[tot] = u;
	sz[u] = 1;
	for (int i = 1; i <= 20; i++) {
		if ((1 << i) > dep[u]) break;
		jp[u][i] = jp[jp[u][i - 1]][i - 1];
	}
	for (int i = head[u]; i; i = e[i].nt) {
		int v = e[i].b;
		if (v == prt[u]) continue;
		prt[v] = u; dep[v] = dep[u] + 1; jp[v][0] = u;
		dfs1(v);
		sz[u] += sz[v];
		if (sz[v] > sz[son[u]]) son[u] = v;
	}
	sec[u] = ++tot;
	ord[tot] = u;
}
inline void dfs2(int u, int chain) {
	bel[u] = chain;
	pos[u] = ++id;
	h[id] = u;
	if (son[u]) dfs2(son[u], chain);
	for (int i = head[u]; i; i = e[i].nt) {
		int v = e[i].b;
		if (v == prt[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
//SEG-----------------------------------------------------------
inline void pushup(int p) {
	T1[p].sum = T1[ls].sum + T1[rs].sum;
	if (T1[ls].rc == T1[rs].lc) --T1[p].sum;
	T1[p].lc = T1[ls].lc, T1[p].rc = T1[rs].rc;
}
inline void build(int p, int l, int r) {
	if (l == r) { T1[p].lc = T1[p].rc = val[h[l]]; T1[p].sum = 1; return; }
	build(ls, l, mid), build(rs, mid + 1, r);
	pushup(p);
}
inline void update(int p, int x, int v, int l = 1, int r = n) {
	if (l == r) { T1[p].lc = T1[p].rc = v; T1[p].sum = 1; return; }
	if (x <= mid) update(ls, x, v, l, mid);
	else update(rs, x, v, mid + 1, r);
	pushup(p);
}
inline int query_seg(int p, int L, int R, int l = 1, int r = n) {
	if (l == L && r == R) return T1[p].sum;
	if (R <= mid) return query_seg(ls, L, R, l, mid);
	else if (L > mid) return query_seg(rs, L, R, mid + 1, r);
	else {
		int ret = query_seg(ls, L, mid, l, mid) + query_seg(rs, mid + 1, R, mid + 1, r);
		if (T1[ls].rc == T1[rs].lc) --ret;
		return ret;
	}
}
inline int leaf_seg(int p, int x, int l = 1, int r = n) {
	if (l == r) return T1[p].lc;
	if (x <= mid) return leaf_seg(ls, x, l, mid);
	else return leaf_seg(rs, x, mid + 1, r);
}
inline int query_chain(int x, int y) {
	int ret = 0, down, up;
	while (bel[x] != bel[y]) {
		if (dep[bel[x]] < dep[bel[y]]) jh(x, y);
		ret += query_seg(1, pos[bel[x]], pos[x]);
		down = leaf_seg(1, pos[bel[x]]);
		up = leaf_seg(1, pos[prt[bel[x]]]);
		if (down == up) --ret;
		x = prt[bel[x]];
	}
	if (dep[x] < dep[y]) jh(x, y);
	ret += query_seg(1, pos[y], pos[x]);
	return ret;
}
//query1-----------------------------------------------------
//MODUI------------------------------------------------------
inline int LCA(int x, int y) {
	if (dep[x] < dep[y]) jh(x, y);
	int t = dep[x] - dep[y];
	for (int i = 0; i <= 20 && t; i++, t >>= 1)
		if (t & 1) x = jp[x][i];
	if (x == y) return x;
	for (int i = 20; i >= 0; i--) if (jp[x][i] != jp[y][i])
		x = jp[x][i], y = jp[y][i];
	return prt[x];
}
inline void add(int x) {
	if (cnt[val[x]]) --out[cnt[val[x]]];
	++cnt[val[x]]; ++out[cnt[val[x]]];
	Max(res, cnt[val[x]]);
}
inline void del(int x) {
	if (cnt[val[x]]) --out[cnt[val[x]]];
	--cnt[val[x]]; ++out[cnt[val[x]]];
	if (!out[res]) --res;
}
inline void change(int x) {
	int aim = chg[x].aim;
	if (use[aim]) del(aim);
	jh(val[aim], chg[x].val);
	if (use[aim]) add(aim);
}
inline void gotime(int x) {
	while (cur < cntc && chg[cur + 1].tim <= x) change(++cur);
	while (cur && chg[cur].tim > x) change(cur--);
}
inline void work(int x) {
	use[x] ^= 1;
	if (use[x]) add(x); else del(x);
}
//ENDMO------------------------------------------------------
int main() {
	freopen("cw.in", "r", stdin);
	freopen("cw.out", "w", stdout);
	read(n), read(Q);
	for (int i = 1; i <= n; i++) read(val[i]);
	for (int i = 1; i < n; i++) {
		int u, v; read(u), read(v);
		anode(u, v); anode(v, u);
	}
	dfs1(1); dfs2(1, 1);
	build(1, 1, n);
	for (int i = 1; i <= Q; i++) {
		int opt, x, y; read(opt), read(x), read(y);
		query[i].opt = opt, query[i].x = x, query[i].y = y;
		if (opt == 1) chg[++cntc].aim = x, chg[cntc].val = y, chg[cntc].tim = i;
		if (opt == 3) {
			if (dep[x] < dep[y]) jh(x, y);
			int lca = LCA(x, y);
			++cnta, ask[cnta].aim = i, ask[cnta].tim = i;
			if (lca == y) ask[cnta].l = fir[y], ask[cnta].r = fir[x], ask[cnta].lca = 0;
			else ask[cnta].l = sec[y], ask[cnta].r = fir[x], ask[cnta].lca = lca;
		}
	}
	blo = pow(tot, 0.666666);
	sort(ask + 1, ask + 1 + cnta);
	for (int i = 1; i <= cnta; i++) {
		gotime(ask[i].tim);
		while (pl < ask[i].l) work(ord[pl++]);
		while (pl > ask[i].l) work(ord[--pl]);
		while (pr < ask[i].r) work(ord[++pr]);
		while (pr > ask[i].r) work(ord[pr--]);
		ans[ask[i].aim] = res;
		if (ask[i].lca && cnt[val[ask[i].lca]] == res) ++ans[ask[i].aim];
	}
	for (int i = 1; i <= Q; i++) {
		if (query[i].opt == 1) update(1, bel[query[i].x], query[i].y);
		else if (query[i].opt == 2) printf("%d\n", query_chain(query[i].x, query[i].y));
		else printf("%d\n", ans[i]);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值