BZOJ 3626 LNOI 2014 LCA 树链剖分

题目大意:给出一棵树,有n个问题,询问在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和。

思路:不会,然后看了题解,之后发现自己智商严重不足。

看到数据范围就知道一定要离线处理,就这个离线处理我估计以我的智商不看题解是肯定想不出来的。。

考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用
[1, r] ? [1, l ? 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n ? 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。

神题啊。。。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
#define INF 0x3f3f3f3f
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define CNT (r - l + 1)
#define MO 201314
using namespace std;

struct _Ask{
	bool is_l;
	int x,z;
	int id;

	_Ask(bool _,int __,int ___,int ____):is_l(_),x(__),z(___),id(____) {}
	_Ask() {}
	bool operator <(const _Ask &a)const {
		if(x == a.x)	return is_l;
		return x < a.x;
	}
}ask[MAX];

struct SegTree{
	long long sum;
	int c;
}tree[MAX << 2];

int points,asks;
int head[MAX],total;
int next[MAX],aim[MAX];

int deep[MAX],father[MAX],son[MAX],size[MAX];
int pos[MAX],top[MAX],cnt;

long long ans[MAX];

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

void PreDFS(int x)
{
	deep[x] = deep[father[x]] + 1;
	size[x] = 1;
	int max_size = 0,p = 0;
	for(int i = head[x]; i; i = next[i]) {
		PreDFS(aim[i]);
		size[x] += size[aim[i]];
		if(max_size < size[aim[i]])
			max_size = size[aim[i]],p = aim[i];
	}
	son[x] = p;
}

void DFS(int x,int _top)
{
	pos[x] = ++cnt;
	top[x] = _top;
	if(son[x])	DFS(son[x],_top);
	for(int i = head[x]; i; i = next[i]) {
		if(aim[i] == son[x])	continue;
		DFS(aim[i],aim[i]);
	}
}

inline void PushDown(int pos,int cnt)
{
	if(tree[pos].c) {
		tree[LEFT].sum += tree[pos].c * (cnt - (cnt >> 1));
		tree[RIGHT].sum += tree[pos].c * (cnt >> 1);
		tree[LEFT].c += tree[pos].c;
		tree[RIGHT].c += tree[pos].c;
		tree[pos].c = 0;
	}
}

void Modify(int l,int r,int x,int y,int pos)
{
	if(l == x && r == y) {
		tree[pos].sum += CNT;
		++tree[pos].c;
		return ;
	}
	PushDown(pos,CNT);
	int mid = (l + r) >> 1;
	if(y <= mid)	Modify(l,mid,x,y,LEFT);
	else if(x > mid)	Modify(mid + 1,r,x,y,RIGHT);
	else {
		Modify(l,mid,x,mid,LEFT);
		Modify(mid + 1,r,mid + 1,y,RIGHT);
	}
	tree[pos].sum = tree[LEFT].sum + tree[RIGHT].sum;
}

inline void Modify(int x)
{
	while(x) {
		Modify(1,cnt,pos[top[x]],pos[x],1);
		x = father[top[x]];
	}
}

long long Ask(int l,int r,int x,int y,int pos)
{
	if(l == x && y == r)
		return tree[pos].sum;
	PushDown(pos,CNT);
	int mid = (l + r) >> 1;
	if(y <= mid)	return Ask(l,mid,x,y,LEFT);
	if(x > mid)		return Ask(mid + 1,r,x,y,RIGHT);
	long long left = Ask(l,mid,x,mid,LEFT);
	long long right = Ask(mid + 1,r,mid + 1,y,RIGHT);
	return left + right;
}

inline long long Ask(int x)
{
	long long re = 0;
	while(x) {
		re += Ask(1,cnt,pos[top[x]],pos[x],1);
		x = father[top[x]];
	}
	return re;
}

int main()
{
	cin >> points >> asks;
	for(int x,i = 2; i <= points; ++i) {
		scanf("%d",&x),++x;
		Add(x,i);
		father[i] = x;
	}
	PreDFS(1);
	DFS(1,1);
	for(int num = 0,x,y,z,i = 1; i <= asks; ++i) {
		scanf("%d%d%d",&x,&y,&z);
		++x,++y,++z;
		ask[++num] = _Ask(true,x - 1,z,i);
		ask[++num] = _Ask(false,y,z,i);
	}
	sort(ask + 1,ask + (asks << 1) + 1);
	int j = 1;
	for(int i = 0; i <= points; ++i) {
		Modify(i);
		for(; ask[j].x == i; ++j)
			if(ask[j].is_l)
				ans[ask[j].id] = Ask(ask[j].z);
			else
				ans[ask[j].id] = (Ask(ask[j].z) - ans[ask[j].id]) % MO;
	}
	for(int i = 1; i <= asks; ++i)
		printf("%d\n",(int)ans[i]);
	return 0;
}

时间: 2024-10-10 16:16:19

BZOJ 3626 LNOI 2014 LCA 树链剖分的相关文章

LCA 树链剖分

//LCA //树链剖分 在线 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,root,cnt,head[500001]; int hvyson[500001],fa[500001],siz[500001],d

Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式)

Count on a tree SPOJ 主席树+LCA(树链剖分实现)(两种存图方式) 题外话,这是我第40篇随笔,纪念一下.<( ̄︶ ̄)[GO!] 题意 是说有棵树,每个节点上都有一个值,然后让你求从一个节点到另一个节点的最短路上第k小的值是多少. 解题思路 看到这个题一想以为是树链剖分+主席树,后来写着写着发现不对,因为树链剖分我们分成了一小段一小段,这些小段不能合并起来求第k小,所以这个想法不对.奈何不会做,查了查题解,需要用LCA(最近公共祖先),然后根据主席树具有区间加减的性质,我们

BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LC

BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

http://www.lydsy.com/JudgeOnline/problem.php?id=3626 LNOI的树链剖分题没有HAOI那么水,学到的东西还是很多的. 我如果现场写,很难想出来这种题,是时候复习一波离线算法泡脑子了.(没有暴力分的题,想不出来正解就爆零,太可怕了) 排序后离线操作通过前缀和计算答案,题解是hzwer的博客上复制的 http://hzwer.com/3891.html 直接引用清华爷gconeice的题解吧 显然,暴力求解的复杂度是无法承受的. 考虑这样的一种暴力

HDOJ 5293 Tree chain problem LCA+树链剖分+树形DP

[题意] 给定一颗树上的几条链和每条链的权值,求能取出的不含有公共节点的链的最大权值.... [解] 预处理每条链的lca 树形DP, d[i]表示取到这个节点时可以得到的最大值 , sum[i]=sigma( d[k] | k 是i的子节点) 如果不取i  d[i]=sum[i] 如果取i , e是lca为i的链则 d[i]=max(d[i],e的权值+sigma(sum[k])-sigma(d[k]))  k为树链上的点 可以用树链剖分+树装数组在nlogn的时间复杂度内求链上的值 Tree

BZOJ 1787: [Ahoi2008]Meet 紧急集合( 树链剖分 )

这道题用 LCA 就可以水过去 , 但是我太弱了 QAQ 倍增写LCA总是写残...于是就写了树链剖分... 其实也不难写 , 线段树也不用用到 , 自己YY一下然后搞一搞就过了...速度还挺快的好像= = #9 ---------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algori

BZOJ 2243:染色(树链剖分+区间合并线段树)

[SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”.请你写一个程序依次完成这m个操作.Input第一行包含2个整数n和m,分别表示节点数和操作数:第二行包含n个正整数表示n个节点的初始颜色下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.下面 行每行描述一个操作:“C

bzoj 3531 [Sdoi2014]旅行(树链剖分,线段树)

3531: [Sdoi2014]旅行 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 876  Solved: 446[Submit][Status][Discuss] Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰.为了方便,我们用不同的正整数代表 各种宗教,  S国的居民常常旅行.旅行时他们总

BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )

树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT+(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. 时间复杂度O(N*log^3(N)) ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<