首页 > 其他分享 >【luogu P1600】天天爱跑步(线段树合并)(LCA)

【luogu P1600】天天爱跑步(线段树合并)(LCA)

时间:2022-11-02 20:02:13浏览次数:80  
标签:return int luogu 线段 fa LCA P1600 now

天天爱跑步

题目链接:luogu P1600

题目大意

有一棵树,给你若干条路径,对于每个点,有一个数 x,求出有多少条路径的第 x 个点是当前点。

思路

考虑把路径拆成两个部分,向上和向下。

考虑对于每个点建一个权值线段树,用深度为下标,记录这样的路径有多少个。(因为向上和向下每个点需要的深度其实是固定的)
(向下的话它可能是先上来在下去的所以我们可以翻折一下把它折到上面的深度即可)

然后至于怎么维护线段树我们可以差分一下,然后就可以用线段树合并来维护这 \(n\) 个线段树了。

(你可以可以直接在每个点记录着修改,然后 dfs 下去用两个桶记录,都可以)

代码

#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 3e5 + 1000;
struct node {
	int to, nxt;
}e[N << 1];
int n, m, w[N], le[N], KK, ans[N];
int deg[N], fa[N][21];

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
}

void dfs(int now, int father) {
	deg[now] = deg[father] + 1;
	fa[now][0] = father;
	for (int i = 1; i <= 20; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) dfs(e[i].to, now);
}

int LCA(int x, int y) {
	if (deg[x] < deg[y]) swap(x, y);
	for (int i = 20; i >= 0; i--)
		if (deg[fa[x][i]] >= deg[y]) x = fa[x][i];
	if (x == y) return x;
	for (int i = 20; i >= 0; i--)
		if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
	return fa[x][0];
}

struct XD_tree {
	int rt[N];
	int f[N << 6], ls[N << 6], rs[N << 6], tot;
	
	void insert(int &now, int l, int r, int pl, int x) {
		if (!now) now = ++tot;
		f[now] += x;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		if (pl <= mid) insert(ls[now], l, mid, pl, x);
			else insert(rs[now], mid + 1, r, pl, x);
	}
	
	int merge(int a, int b, int l, int r) {
		if (!a || !b) return a + b;
		if (l == r) {f[a] += f[b]; return a;}
		int mid = (l + r) >> 1;
		ls[a] = merge(ls[a], ls[b], l, mid);
		rs[a] = merge(rs[a], rs[b], mid + 1, r);
		return a;
	}
	
	int query(int now, int l, int r, int pl) {
		if (!now) return 0;
		if (l == r) return f[now];
		int mid = (l + r) >> 1;
		if (pl <= mid) return query(ls[now], l, mid, pl);
			else return query(rs[now], mid + 1, r, pl);
	}
}T1, T2;

void dfs1(int now, int father) {
	if (now == 80) {
		now++; now--;
	}
	for (int i = le[now]; i; i = e[i].nxt)
		if (e[i].to != father) {
			dfs1(e[i].to, now);
			T1.rt[now] = T1.merge(T1.rt[now], T1.rt[e[i].to], -n, n);
			T2.rt[now] = T2.merge(T2.rt[now], T2.rt[e[i].to], -n, n);
		}
	if (deg[now] + w[now] <= n) ans[now] += T1.query(T1.rt[now], -n, n, deg[now] + w[now]);
	if (deg[now] - w[now] >= -n) ans[now] += T2.query(T2.rt[now], -n, n, deg[now] - w[now]);
}

int main() {
//	freopen("P1600.in", "r", stdin);
//	freopen("P1600.ans", "w", stdout);
	
	scanf("%d %d", &n, &m);
	for (int i = 1; i < n; i++) {
		int x, y; scanf("%d %d", &x, &y);
		add(x, y); add(y, x);
	}
	for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
	
	dfs(1, 0);
	
	for (int i = 1; i <= m; i++) {
		int x, y; scanf("%d %d", &x, &y); int lca = LCA(x, y);
		T1.insert(T1.rt[x], -n, n, deg[x], 1);
		T1.insert(T1.rt[lca], -n, n, deg[x], -1);
		T2.insert(T2.rt[y], -n, n, deg[lca] * 2 - deg[x], 1);
		T2.insert(T2.rt[fa[lca][0]], -n, n, deg[lca] * 2 - deg[x], -1);
	}
	dfs1(1, 0);
	
	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	
	return 0;
}

标签:return,int,luogu,线段,fa,LCA,P1600,now
From: https://www.cnblogs.com/Sakura-TJH/p/luogu_P1600.html

相关文章

  • LCA 的四种求法,你都会了吗?
    回字有四样写法,你知道么?lca,即最近公共祖先,如上图中2和13的lca是1,5和6的lca是2。众所周知,LCA的主流求法有4种。那么,你都会了吗?树链剖分如果你不会......
  • 【XSY2485】MST(最小生成树+倍增lca+并查集)
    题面Description给定一个\(n\)个点\(m\)条边的连通图,保证没有自环和重边。对于每条边求出,在其他边权值不变的情况下,它能取的最大权值,使得这条边在连通图的所有最小生成......
  • 【XSY2444】【BZOJ4042】【CERC2014】【luogu4757】Parades(树形dp+状压dp)
    题面Description从前有个A国,它有\(n\)个城市和\(n-1\)条道路。每条路连接两个城市。城市之间两两可达。每个城市与不超过10条道路相连。现在给出\(m\)条路径,要求从这些......
  • 【XSY2414】【CF587C】Duff in the Army(倍增lca)
    看到题目中\(a<=0\),自然就想到用暴力维护这个东东。设倍增数组\(fa[u][i]\)和\(minn[u][i]\),其中\(minn\)存的是一个结构体,结构体中包含两个东东:一个数组和这个数组中的元......
  • luogu 1908 逆序对板子
     逆序对的本质是二维偏序 给第一维排序(输入时已排好),统计y(k)>=y(i)k<i的个数用树状数组维护y值前缀和,需要的时候直接查询该题需要离散化这个y,再作为树状数组......
  • luogu 4588
    给xx这个数进行操作1m:将 xx 变为x,并输出 x%mod2pos:将 xx 变为 xx 除以第 pos 次操作所乘的数(保证第 pos 次操作一定为类型1,对于每一个类型1的操作至多......
  • Luogu P5658 括号树
    LuoguP5658括号树来补一道当年考场上没做出来的题。不难想到树上DP,关键在于如何设置函数与转移。按题意,记$k_i$为以$s_i$结尾的串中的合法子串数;记$cnt_i$为......
  • 【luogu P6130】随机红包(数学)(期望)
    随机红包题目链接:luoguP6130题目大意把一个数1分成n份,求第k小的期望大小,多次询问。思路首先考虑最小的期望大小,那假设最小的是\(x\),剩下的都大于\(x\)。那......
  • Luogu 2894 酒店Hotel
    题目链接:​​传送门​​题目描述:参考样例,第一行输入n(1≤n≤50,000),m(1≤M<50,000),n代表有n个房间,编号为1—n,开始都为空房,m表示以下有m行操作,以下每行先输入一个......
  • Luogu 3478 [POI2008]STA-Station
    题目链接:​​传送门​​题目描述给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大输入样例814564567682434输出样例7一句话题意好......