首页 > 其他分享 > Luogu P4219 [BJOI2014]大融合

Luogu P4219 [BJOI2014]大融合

时间:2023-06-09 16:44:54浏览次数:52  
标签:ch int Luogu tree BJOI2014 son dfn P4219 5211314

[BJOI2014]大融合

题目描述

小强要在 \(N\) 个孤立的星球上建立起一套通信系统。这套通信系统就是连接 \(N\) 个点的一个树。

这个树的边是一条一条添加上去的。

在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。

例如,在上图中,现在一共有了 \(5\) 条边。其中,\((3,8)\) 这条边的负载是 \(6\),因为有六条简单路径 \(2-3-8\),\(2-3-8-7\),\(3-8,3-8-7\),\(4-3-8\),\(4-3-8-7\) 路过了 \((3,8)\)。

现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。

输入格式

第一行包含两个整数 \(N, Q\),表示星球的数量和操作的数量。星球从 \(1\) 开始编号。

接下来的 \(Q\) 行,每行是如下两种格式之一:

  • A x y 表示在 \(x\) 和 \(y\) 之间连一条边。保证之前 \(x\) 和 \(y\) 是不联通的。
  • Q x y表示询问 \((x,y)\) 这条边上的负载。保证 \(x\) 和 \(y\) 之间有一条边。

输出格式

对每个查询操作,输出被查询的边的负载。

样例 #1

样例输入 #1

8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8

样例输出 #1

6

提示

对于所有数据,\(1≤N,Q≤10^5\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define lid (tree[id].lson)
#define rid (tree[id].rson)

using namespace std;

int fa[5211314];
char ch[5211314];
int n, q, tot;
int cnt, head[5211314], nex[5211314], to[5211314];
int ask1[5211314], ask2[5211314];
int dfn[5211314], max_dfn[5211314], newid;
bool flag[5211314];
int anc[5211314], root[5211314];

struct Segment_Tree {
	int lson, rson;
	int sum;
}tree[5211314];

inline void AddPoi(int v1, int v2) {
	++ cnt;
	nex[cnt] = head[v1];
	head[v1] = cnt;
	to[cnt] = v2;
	return;
}

inline int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch - '0');
		ch = getchar();
	}
	return x * f;
}

void DFS(int now, int ancestor) {
	anc[now] = ancestor;
	//记录当前节点所在树的祖先
	max_dfn[now] = dfn[now] = ++ newid;
	//将当前节点子树的dfn处理出来
	for (int i = head[now]; i != 0; i = nex[i]) {
		if (flag[to[i]] == false) {
			flag[to[i]] = true;
			DFS(to[i], ancestor);
			max_dfn[now] = max_dfn[to[i]];
		}
	}
	return;
}

struct MergeSegmentTree {
	inline void PushUp(int id) {
		tree[id].sum = tree[lid].sum + tree[rid].sum;
		return;
	}
	void Update(int &id, int l, int r, int pos) {
		if (id == 0) id = ++ tot;
		if (l == r) {
			tree[id].sum ++;
			return;
		}
		int mid = (l + r) >> 1;
		if (pos <= mid) Update(lid, l, mid, pos);
		else Update(rid, mid + 1, r, pos);
		PushUp(id);
		return;
	}
	int Merge(int a, int b, int l, int r) {
		if (!a || !b) return a + b;
		if (l == r) {
			tree[a].sum += tree[b].sum;
			return a;
		}
		int mid = (l + r) >> 1;
		tree[a].lson = Merge(tree[a].lson, tree[b].lson, l, mid);
		tree[a].rson = Merge(tree[a].rson, tree[b].rson, mid + 1, r);
		PushUp(a);
		return a;
	}
	int Query(int id, int l, int r, int vl, int vr) {
		if (id == 0) return 0;
		if (vl <= l && r <= vr) {
			return tree[id].sum;
		}
		int mid = (l + r) >> 1, ans = 0;
		if (vl <= mid) ans += Query(lid, l, mid, vl, vr);
		if (mid < vr) ans += Query(rid, mid + 1, r, vl, vr);
		return ans; 
	}
}t;

int Find(int x) {
	if (fa[x] == x) return x;
	else return fa[x] = Find(fa[x]);
}

int main() {
	n = read();
	q = read();
	for (int i = 1; i <= q; ++ i) {
		ch[i] = getchar();
		while (ch[i] != 'A' && ch[i] != 'Q') ch[i] = getchar();
		ask1[i] = read();
		ask2[i] = read();
		if (ch[i] == 'A') {
			AddPoi(ask1[i], ask2[i]);
			AddPoi(ask2[i], ask1[i]);
		}
	}
	for (int i = 1; i <= n; ++ i) {
		fa[i] = i;
		if (flag[i] == false) {
			flag[i] = true;
			DFS(i, i);//离线处理每个点的dfn
		}
	}
	for (int i = 1; i <= n; ++ i) {
		t.Update(root[i], 1, n, dfn[i]);
		//注意插入要按dfn的值插入 并且要在跑完dfn后插入 
	}
	for (int i = 1; i <= q; ++ i) {
		if (ch[i] == 'A') {
			int x = Find(ask1[i]);
			int y = Find(ask2[i]);
			//记得找ask1[i]和ask2[i]所在的线段树的根
			fa[y] = x;
			t.Merge(root[x], root[y], 1, n);
			//进行合并
		}
		else {
			int rt = Find(ask1[i]), son;
			int l1 = dfn[anc[ask1[i]]], r1 = max_dfn[anc[ask1[i]]];
			if (dfn[ask1[i]] > dfn[ask2[i]]) son = ask1[i];
			else son = ask2[i];//找dfn大的点(即找为儿子点)
			int ans_anc = t.Query(root[rt], 1, n, l1, r1);
			int ans_son = t.Query(root[rt], 1, n, dfn[son], max_dfn[son]);
			//儿子节点dfn范围内的sum值即为儿子节点所在连通块的大小
			//祖先的dfn范围内的sum减去儿子点dfn范围内的sum即为父亲节点所在连通块的大小
			printf("%d\n", (ans_anc - ans_son) * ans_son);
			//边左边的点的个数乘边右边的点的个数即为边的负载
		}
	}
	return 0;
}

标签:ch,int,Luogu,tree,BJOI2014,son,dfn,P4219,5211314
From: https://www.cnblogs.com/jueqingfeng/p/17469620.html

相关文章

  • Luogu P4577 [FJOI2018] 领导集团问题
    [FJOI2018]领导集团问题题目描述一个公司的组织领导架构可以用一棵领导树来表示。公司的每个成员对应于树中一个结点\(v_i\),且每个成员都有响应的级别\(w_i\)。越高层的领导,其级别值\(w_i\)越小。树中任何两个结点之间有边相连,则表示与结点相应的两个成员属于同一部门。领......
  • Luogu P5446 [THUPC2018] 绿绿和串串
    根据题目能发现一个性质,设转化前的字符串为\(s\),转化后的字符串为\(s'\),则\(s'_{|s|}\)为\(s'\)的中心,即\(s'_{|s|}\)求出来的回文串左边界为\(1\)右边界为\(|s'|\)找出这个性质就挺简单了,考虑先对给出的\(S\)用\(\text{manacher}\)求出每个节点\(i\)对应的左边......
  • Luogu P3224 [HNOI2012]永无乡
    [HNOI2012]永无乡题目描述永无乡包含\(n\)座岛,编号从\(1\)到\(n\),每座岛都有自己的独一无二的重要度,按照重要度可以将这\(n\)座岛排名,名次用\(1\)到\(n\)来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛\(a\)出发经过若干座(含\(0\)......
  • [刷题笔记] Luogu P3073 [USACO13FEB]Tractor S
    ProblemSolution和汽车拉力比赛差不多,思路都是二分,二分\(d\),但是汽车拉力比赛从一个路标开始搜即可,本题没有给定起点。一条合法路径起点是未知的,不得随便从一个点开始搜,否则可能找不到正确路径。怎么处理呢?容易想到对于每一个二分的\(d\),开一个\(n^2\)的循环,从每一个点开始搜......
  • Luogu P3605 [USACO17JAN]Promotion Counting P
    [USACO17JAN]PromotionCountingP题目描述Thecowshaveonceagaintriedtoformastartupcompany,failingtorememberfrompastexperiencethatcowsmaketerriblemanagers!Thecows,convenientlynumbered\(1\ldotsN\)(\(1\leqN\leq100,000\)),o......
  • Luogu P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并
    [Vani有约会]雨天的尾巴/【模板】线段树合并题目背景深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无奈......
  • LuoguP4318 完全平方数
    标签:莫比乌斯函数,容斥完全平方数题目描述小X自幼就很喜欢数。但奇怪的是,他十分讨厌完全平方数。他觉得这些数看起来很令人难受。由此,他也讨厌所有是完全平方数的正整数倍的数。然而这丝毫不影响他对其他数的热爱。这天是小X的生日,小W想送一个数给他作为生日礼物。当然他......
  • Luogu P2709 小B的询问
    https://www.luogu.com.cn/problem/P2709#submit小B的询问题目描述小B有一个长为\(n\)的整数序列\(a\),值域为\([1,k]\)。他一共有\(m\)个询问,每个询问给定一个区间\([l,r]\),求:\[\sum\limits_{i=1}^kc_i^2\]其中\(c_i\)表示数字\(i\)在\([l,r]\)中的出现次数......
  • [刷题笔记] Luogu P2895 Meteor Shower S
    ProblemSolution显然bfs,只不过有了限定条件,有实时的流星雨这里提供两种做法:Solution1这也是我一开始的做法模拟实时流星,由于bfs是按层搜的,是严格按照时间递增搜的,故可以模拟实时放流星。需要注意放流星的时间,如果第\(t\)秒有流星,则该秒不可以走,需要在每一秒前放流星。那......
  • [刷题笔记] LuoguP2658 汽车拉力比赛
    ProblemSolution需要找到最小满足题意的\(d\),显然\(d\)满足单调性,考虑二分二分\(d\),然后直接bfs,每次bfs判断能不能走的时候还需要加上高度差不超过二分的\(d\)(即满足),bfs跑完后看看所有的路标都被访问了没。(可以记录个数,因为不可能重复走)二分的时候注意\(l\)从0开始,不然会WA......