首页 > 其他分享 >[POI2003] Monkeys 题解

[POI2003] Monkeys 题解

时间:2023-10-01 21:45:08浏览次数:34  
标签:ch int 题解 dsu POI2003 vc 猴子 siz Monkeys

[POI2003] Monkeys 题解

正着做貌似不好做,发现猴子是否掉落取决于“最后一根稻草”,也就是最后撒手的那个猴子,那我们考虑倒着把猴子网拼回去。这样,每群猴子掉落的时刻就是与 \(1\) 号猴子连通的时刻。

利用并查集可以维护猴子的连通性,但是怎么更新答案呢?这里用 vector 进行了一个猴子的存。没错,就是暴力更新,因为每只猴子只会被更新一次;但是合并呢?如果暴力合并复杂度肯定起飞,所以考虑启发式合并,让 \(siz\) 小的向大的上面合并,这样,一个点每被遍历一次,\(siz\) 都会翻倍,时空复杂度都是 \(n \log n\)。

一开始炸空间了还以为是内存管理的问题,后来又 TLE 了才发现是忘了更新 \(siz\) 了,内存管理加不加都无所谓。

代码:

/*
正着很难推,但是如果倒着考虑……
把与 1 联通的时刻作为掉下来的时刻就好了吧
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;

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*10+(ch-48), ch = getchar();
	return x * f;
}
struct Monkey {
	int lh, rh;
	bool tagl, tagr;
}a[N];

int n;
vector<int> vc[N];
struct DSU{
	int fa[N];
	int siz[N];
	void init() {
		for(int i = 1; i<=n; ++i) {
			fa[i] = i;
			siz[i] = 1;
			vc[i].push_back(i);
		}
	}
	int find(int x) {
		if(x != fa[x]) fa[x] = find(fa[x]);
		return fa[x];
	}
	void merge(int x, int y) {
		x = find(x), y = find(y);
		if(x == y) return;
		if(siz[x] > siz[y]) swap(x, y);
		fa[x] = y;
		siz[y]+=siz[x];
		for(int i: vc[x]) {
			vc[y].push_back(i);
		}
		vc[x].clear();
//		vc[x].shrink_to_fit();无所谓的内存管理。
		return;
	}
}dsu;
struct OPT {
	int id, op;
}b[N<<1];
int m;
int ans[N];
int main() {
	memset(ans, -1, sizeof(ans));
	n = read(), m = read();
	for(int i = 1; i<=n; ++i) {
		a[i].lh = read(), a[i].rh = read();
	}
	dsu.init();
	for(int i = 1; i<=m; ++i) {
		b[i].id = read(), b[i].op = read()-1;
		if(b[i].op) a[b[i].id].tagr = 1;
		else a[b[i].id].tagl = 1;
	}
	for(int i = 1; i<=n; ++i) {
		if(a[i].lh!=-1 && (!a[i].tagl)) {
			dsu.merge(i, a[i].lh);
		}
		if(a[i].rh!=-1 && (!a[i].tagr)) {
			dsu.merge(i, a[i].rh);
		}
	}
	for(int i = m; i>=1; --i) {
		int x = b[i].id, y = a[b[i].id].lh;
		if(b[i].op) y = a[b[i].id].rh;
		if(y == -1) continue;
		x = dsu.find(x), y = dsu.find(y);
		if(x == y) continue;
		int tmp = dsu.find(1);
		if(x != tmp && y != tmp) {
			dsu.merge(x, y);
		} else {
			if(x != tmp) swap(x, y);
			for(int u:vc[y]) {
				ans[u] = i-1;
			}
			dsu.merge(x, y);
		}
	}
	for(int i = 1; i<=n; ++i) {
		printf("%d\n", ans[i]);
	}
	return 0;
}

标签:ch,int,题解,dsu,POI2003,vc,猴子,siz,Monkeys
From: https://www.cnblogs.com/frostwood/p/17739319.html

相关文章

  • CF1874C Jellyfish and EVA 题解
    题意给定一个有向无环图,对于任意一条边\((u_i,v_i)\),有\(u_i<v_i\)。定义一次从节点\(u\)开始的移动为如下过程:\(\tt{Alice}\)选择从\(u\)出发的且未被删除的一条边。\(\tt{Bob}\)在从\(u\)出发的且未被删除的边中等概率随机选择一条。若两人选择的边相同......
  • P1126 机器人搬重物 题解
    Problem题目概括$n\timesm$的网格,有些格子是障碍格。\(0\)无障碍,\(1\)有障碍。机器人有体积,总是在格点上。有5种操作:向前移动\(1/2/3\)步左转\(/\)右转每次操作需要\(1\)秒。求从\(x_1,y_1\)到\(x_2,y_2\)点的最短路。机器人有一个初始方向$......
  • P1182 数列分段 Section II 题解
    Problem考察知识点:二分、贪心。题目描述对于给定的一个数组,现要将其分成\(M\)段,并要求每段连续,且每段和的最大值最小。思路二分答案出每段和最大值的最小值,然后贪心检验是否满足。难点在\(check\)上。策略:每次开始循环,如果没有超范围,就一直选,知道选满为止,求最大值。代......
  • Codeforces 1278D 题解
    题目大意题目大意给你\(n\)(\(1\leqslantn\leqslant5\cdot10^5\))条线段\([l_1,r_1],[l_2,r_2],\cdots,[l_n,r_n]\)(\(1\lel_i<r_i\le2n\))。保证每条线段的端点为整数,且\(\foralli,j\)(\(i\nej\)),不存在\(l_i=l_j\)或\(r_i=r_j\),不存......
  • P5943 [POI2002] 最大的园地 题解
    题目传送门前置知识单调栈简化题意在一个\(n\timesn\)的正方形内找到最大的由\(0\)组成的子矩形的面积。解法令\(f_{i,j}(1\lei,j\len)\)表示从\((1,j)\)到\((i,j)\)中以\((i,j)\)结尾的均为\(0\)的子串长度,即\((i,j)\)上面可延伸的最大距离(子矩形的......
  • Codeforces 1702G2 题解
    题目大意给出一个大小为\(n\)的树,\(q\)次询问,每次给出一个大小为\(m\)的点集,判断是否有一条链覆盖这些点(这条链可以经过其他点)。\(n,\summ\leqslant2\cdot10^5\),\(q\leqslant10^5\)。提示提示1思考将$m$个点按深度排序。题解题解考虑将\(m\)个点按树......
  • ABC322G题解
    这场的G怎么这么毒瘤啊/kk听说正解是DP?我爆搜头一个表示不服!statement找出三元组\((S,a,b)\)的数量,使得\(S\)在\(a\)进制下和在\(b\)进制下的差为\(X\),其中\(0\leqS_i<(min(a,b,10))\)。首先因为\(X>0\)显然\(S\)不可能为\(1\)位数。如果\(S\)......
  • 「题解」Codeforces Round 895 (Div. 3)
    A.TwoVesselsProblem题目Sol&Code签到题#include<bits/stdc++.h>typedeflonglongll;intmin(inta,intb){returna<b?a:b;}intmax(inta,intb){returna>b?a:b;}intT,a,b,c;intmain(){scanf("%d"......
  • AT_abc254_h [ABC254Ex] Multiply or Divide by 2 题解
    打篇题解巩固一下。题意给你两个集合\(A\)和\(B\),对于任意\(A\)集合中的元素,我们可以进行\(2\)种操作:\(a_i\gets\left\lfloor\frac{a_i}{2}\right\rfloor\)或\(a_i\gets2\timesa_i\)。问最少要多少次才能使\(A\)和\(B\)中的元素相等。分析首先我们可以令\(a......
  • UVA12655 Trucks 题解
    题目传送门前言中文题目可以看link。前置知识Kruskal重构树|最近公共祖先简化题意给定一个\(N\)个点\(M\)条边的有向图,共有\(S\)次询问,每次询问从\(L\)到\(H\)所有的路径中最小的权值的最大值(多组数据)。本题即最大瓶颈路问题。解法使最小的权值最大,不难......