【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)

学习了树上莫队,树分块后对讯问的$dfs序$排序,然后就可以滑动树链处理答案了。

关于树链的滑动,只需要特殊处理一下$LCA$就行了。

在这里一条树链保留下来给后面的链来转移的$now$的为这条树链上所有点除去$LCA$的颜色种数。因为如果要考虑$LCA$情况就太多了,不如单独考虑$LCA$。

转移后加上当前链的$LCA$进行统计,然后再去掉这个$LCA$更新一下$now$值给后面的链转移。

这都是我的理解,说的有点不清楚,具体请看vfk的题解 OTZ 虽然不是这道题,但是通过这篇博客学习树上莫队也是很好的。

PS:压行大法使代码看起来像一堵墙

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50003
#define M 100003
#define read(x) x=getint()
using namespace std;
inline int getint() {int k = 0, fh = 1; char c = getchar();	for(; c < ‘0‘ || c > ‘9‘; c = getchar()) if (c == ‘-‘) fh = -1; for(; c >= ‘0‘ && c <= ‘9‘; c = getchar()) k = k * 10 + c - ‘0‘; return k * fh;}
int n, m, color[N], cnt = 0, fa[N][16], deep[N], dfn[N << 1], now = 0;
int belong[N], cntblo = 0, sqrblo, top = 0, sta[N], ans[M], colsum[N], point[N];
short v[N];
struct Enode {int nxt, to;} E[N << 1];
struct node {int x, y, a, b, id;} q[M];
inline bool cmp(node A, node B) {return belong[A.x] == belong[B.x] ? dfn[A.y] < dfn[B.y] : dfn[A.x] < dfn[B.x];}
inline void ins(int x, int y) {E[++cnt].nxt = point[x]; E[cnt].to = y; point[x] = cnt;}

inline void dfs(int x) {
	dfn[x] = ++cnt;
	int mark = top;
	for(int i = 1; i <= 15; ++i)
		fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(int tmp = point[x]; tmp; tmp = E[tmp].nxt) {
		int v = E[tmp].to;
		if (v == fa[x][0]) continue;
		deep[v] =deep[x] + 1;
		fa[v][0] = x;
		dfs(v);
		if (top - mark >= sqrblo) {
			++cntblo;
			while (top != mark)
				belong[sta[top--]] = cntblo;
		}
	}
	sta[++top] = x;
}

inline int LCA(int x, int y) {
	if (deep[x] < deep[y])
		swap(x, y);
	int k = deep[x] - deep[y];
	for(int j = 15; j >= 0; --j)
		if (k & (1 << j))
			x = fa[x][j];
	if (x == y) return x;
	for(int j = 15; j >= 0; --j)
		if (fa[x][j] != fa[y][j])
			x = fa[x][j], y = fa[y][j];
	return fa[x][0];
}

inline void pushup(int x) {
	if (v[x]) {
		--colsum[color[x]];
		if (!colsum[color[x]])
			--now;
	} else {
		if (!colsum[color[x]])
			++now;
		++colsum[color[x]];
	}
	v[x] ^= 1;
}

inline void change(int x, int y) {
	while (x != y) {
		if (deep[x] < deep[y])
			pushup(y), y = fa[y][0];
		else
			pushup(x), x = fa[x][0];
	} //O)Z这个方法好神啊!!!我为什么想不到一个一个往上跳呢QAQ
}

int main() {
	read(n); read(m);
	for(int i = 1; i <= n; ++i)
		read(color[i]);
	int u, v;
	for(int i = 1; i <= n; ++i) {
		read(u); read(v);
		if (u == 0 || v == 0) continue;
		ins(u, v);
		ins(v, u);
	}
	sqrblo = ceil(sqrt(n));
	cnt = 0;
	dfs(1);
	while (top)
		belong[sta[top--]] = cntblo;

	for(int i = 1; i <= m; ++i) {
		read(q[i].x); read(q[i].y); read(q[i].a); read(q[i].b); q[i].id = i;
		if (dfn[q[i].x] > dfn[q[i].y])
			swap(q[i].x, q[i].y);
	}

	sort(q + 1, q + m + 1, cmp);
	q[0].x = q[0].y = 1;

	for(int i = 1; i <= m; ++i) {
		change(q[i - 1].x, q[i].x);
		change(q[i - 1].y, q[i].y);
		int lca = LCA(q[i].x, q[i].y);
		pushup(lca);
		ans[q[i].id] = now;
		if (colsum[q[i].a] && colsum[q[i].b] && q[i].a != q[i].b)
			--ans[q[i].id];
		pushup(lca);
	}

	for(int i = 1; i <= m; ++i)
		printf("%d\n", ans[i]);
	return 0;
}

$SDOI2016 Round1$之前做的最后一道题了,希望省选不要爆零啊$QAQ$

时间: 2024-08-10 02:10:21

【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)的相关文章

BZOJ 3757 苹果树 树上莫队

题目大意:给出一棵树,问任意两点之间有多少种不同的颜色,一个人可能会有色盲,会将A和B当成一种颜色. 思路:比较裸的树上莫队,写出来之后,很慢,怀疑是分块的缘故,然后果断找了当年比赛的标称交上去,瞬间rk1,大概看了一眼,他好像是直接用DFS序+曼哈顿距离最小生成树搞的,为什么会比分块快? 昨天下午看到这个题之后就一直在研究树上莫队的正确姿势,然后先写了树分块,后来看了很多牛人的SPOJ COT2的题解,后来又和同学探讨了好久才弄明白. 首先先将树分块,然后把区间排序,按照第一权值为左端点所在块

【BZOJ】3052: [wc2013]糖果公园 树分块+待修改莫队算法

[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的和或修改一个点的糖果ci.n,m,q<=10^5. [算法]树分块+带修改莫队算法 [题解]参考:WC 2013 糖果公园 park 题解 by vfleaking 首先树分块,参考王室联邦的方法.确定块大小为B,一遍DFS可以分成若干大小为[B,3B]的块,性质是块内两点距离至多为B. 定义(x,

[BZOJ 1086] [SCOI2005] 王室联邦 【树分块】

题目链接:BZOJ - 1086 题目分析 这道题要求给树分块,使得每一块的大小在 [B, 3B] 之间,并且可以通过一个块外的节点(块根)使得整个块联通. 那么我们使用一种 DFS,维护一个栈,DFS 完一个节点 x 的所有子树后,就将 x 压入栈内. 我们不能简单的判断栈内元素 >= B 就将栈中的元素弹出来作为一个块,因为这样可能是遍历一棵子树后剩下的一些节点和另一棵子树中的一些节点在一起,然而它们并不连通. 所以,我们需要记录一下对于 x 的栈底,即 DFS(x) 之前的栈顶,需要判断当

BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法

题目大意:给定一个序列.求一个区间内有多少个不同的数 正解是树状数组 将全部区间依照左端点排序 然后每次仅仅统计左端点開始的每种颜色的第一个数即可了 用树状数组维护 我写的是莫队算法 莫队明显能搞 m√m明显慢了点可是还是能接受的一个复杂度 一開始离散化数组开小了各种秒RE-- 跪了 #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algori

bzoj 3757: 苹果树(树上莫队)

3757: 苹果树 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 1327  Solved: 510 [Submit][Status][Discuss] Description 神犇家门口种了一棵苹果树.苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条.由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色.我们用一个1到n之间的正整数来表示一种颜色

bzoj 3289: Mato的文件管理 莫队+树状数组

3289: Mato的文件管理 Time Limit: 40 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号.为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问.Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料.Mato有一个习惯,他总是从文件大小从小到大看资料.他先

BZOJ 1086 [SCOI2005]王室联邦 树分块

题意:链接 方法:树分块 解析: 为了去刷莫队上树所以来学习树分块,听说这是裸题所以跑来搞. 树分块的过程是什么? 从树根向下递归搜索,如果回溯的节点超过了我们想分成的块的大小(不妨设为a),就将这些节点作为一个块,并且回溯到的这个节点是与该块中的任意节点连通的. 这里显然可以用一个栈来处理. 并且需要注意的是,如果我们不加以限制,会出现什么结果呢?会使分的块中的元素碎成渣,即瞎分. 什么限制呢? 是对于每个节点的相对栈底的限制. 如果在当前节点,其有两个儿子. 在左儿子的子树中搜得的节点数是a

51nod 1290 Counting Diff Pairs | 莫队 树状数组

51nod 1290 Counting Diff Pairs | 莫队 树状数组 题面 一个长度为N的正整数数组A,给出一个数K以及Q个查询,每个查询包含2个数l和r,对于每个查询输出从A[i]到A[j]中,有多少对数,abs(A[i] - A[j]) <= K(abs表示绝对值). 题解 莫队!//其实我就是搜索"51nod + 莫队"找到的这道题-- 七级算法题! 一道320分! 你值得拥有! 题解就是--用个普通的莫队,加上树状数组来统计符合条件的数个数,就好啦. 当增加/

HihoCoder 1488 : 排队接水(莫队+树状数组)

描述 有n个小朋友需要接水,其中第i个小朋友接水需要ai分钟. 由于水龙头有限,小Hi需要知道如果为第l个到第r个小朋友分配一个水龙头,如何安排他们的接水顺序才能使得他们等待加接水的时间总和最小. 小Hi总共会有m次询问,你能帮助他解决这个问题吗? 假设3个小朋友接水的时间分别是2,3,4.如果他们依次接水,第一位小朋友等待加接水的时间是2,第二位小朋友是5,第三位小朋友是9.时间总和是16. 输入 第一行一个数T(T<=10),表示数据组数 对于每一组数据: 第一行两个数n,m(1<=n,m