ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P5064 [Ynoi2014] 等这场战争结束之后 题解

2022-07-10 18:05:11  阅读:151  来源: 互联网

标签:int 题解 Ynoi2014 unsigned P5064 fx MAXN fy 操作


毒瘤卡常分块题但是看你写法。

注意到这道题有回溯操作,可以考虑建立操作树,下设第 \(i\) 个操作在操作树上对应的节点为 \(p_i\):

先建立 0 号节点表示初始状态(令 \(p_0=0\)),对于 1,3 两个非回溯操作(设为第 \(i\) 个操作),当前操作编号就是操作树上对应的节点编号,即令 \(p_i=i\);对于 2 这个回溯操作,由于我们知道其会回溯到哪一步(设为第 \(j\) 步),则令 \(p_i = p_j\)。

最后对 1,3 操作,连边 \(p_{i-1},p_i\),边上的权值就是当前操作。

这样做,我们就成功的解决了这个烦人的回溯操作,因为我们只需要遍历这棵操作树就可以得到答案,而遍历操作树时我们只需要考虑一条边的插入与删除,和一个询问,和线段树分治有点像。

然后先对权值离散化,使用可撤销并查集维护当前图的连通性,对每一个点我们进行值域分块,每一个点在并查集上的根节点维护当前所在连通块的权值,查询的时候在对应的根节点上查询第 k 小。

接下来讲讲我的写法:

  1. 由于我的写法特别卡(#16 499ms AC),所以我的代码不见得有参考价值(但是对的),请谨慎参考。
  2. 实际操作中建议采用启发式合并维护并查集,虽然这比按轶合并慢但是操作 3 出现 k > 当前子树大小时可以直接判掉。
  3. 建议多封装几个函数(但是不要太多,封装比较复杂的操作),比如说 dfs 中对于一条边的合并、撤销以及查询操作,可以分别写 3 个函数维护,实测比全堆在 dfs 函数中快。
  4. unsigned int 比 int 快。

CodeBase:CodeBase of Plozia

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P5064 [Ynoi2014] 等这场战争结束之后
	Date:2022/7/9
========= Plozia =========
*/

#include <bits/stdc++.h>
typedef long long LL;

const int MAXN = 1e5 + 1;
unsigned int n, m, Head[MAXN], cntEdge, Block = 4250;
int ans[MAXN];
unsigned int fa[MAXN], Size[MAXN];
unsigned short f[MAXN][25];
struct node { unsigned int opt, x, y; } q[MAXN];
struct EDGE { unsigned int To, Next; } Edge[MAXN];
struct NODE
{
	unsigned int a, id;
	bool operator <(const NODE &fir)const { return a < fir.a; }
}a[MAXN];

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum * 10) + (ch ^ 48);
	return sum * fh;
}
void addEdge(int From, int To)
{
	++cntEdge; Edge[cntEdge] = (EDGE){To, Head[From]}; Head[From] = cntEdge;
}
int gf(int x)
{
	while (fa[x] != x) x = fa[x];
	return x;
}

void Merge(int &fx, int &fy)
{
	if (Size[fx] > Size[fy]) fx ^= fy ^= fx ^= fy;
	fa[fx] = fy; Size[fy] += Size[fx];
	for (int j = 1; j <= 24; ++j) f[fy][j] += f[fx][j];
}
void Split(int fx, int fy)
{
	fa[fx] = fx; Size[fy] -= Size[fx];
	for (int j = 1; j <= 24; ++j) f[fy][j] -= f[fx][j];
}

void Calc(int fx, int u)
{
	int sum = 0, flag = 0;
	for (int j = 1; j <= 24; ++j)
	{
		if (sum + f[fx][j] < q[u].y) sum += f[fx][j];
		else { flag = j; break ; }
	}
	for (int j = (flag - 1) * Block + 1, pz = std::min(flag * Block, n); j <= pz; ++j)
	{
		if (gf(a[j].id) == fx) ++sum;
		if (sum == q[u].y) { ans[u] = a[j].a; break ; }
	}
}

void dfs(int now)
{
	for (int i = Head[now]; i; i = Edge[i].Next)
	{
		int u = Edge[i].To;
		if (q[u].opt == 1)
		{
			int fx = gf(q[u].x), fy = gf(q[u].y);
			if (fx != fy) Merge(fx, fy);
			dfs(u);
			if (fx != fy) Split(fx, fy);
		}
		else if (q[u].opt == 3)
		{
			int fx = gf(q[u].x);
			if (q[u].y > Size[fx]) { ans[u] = -1; dfs(u); continue ; }
			Calc(fx, u); dfs(u);
		}
		else dfs(u);
	}
}

int main()
{
	n = Read(), m = Read(); for (int i = 1; i <= n; ++i) a[i].a = Read(), a[i].id = i;
	std::sort(a + 1, a + n + 1);
	for (int i = 1; i <= m; ++i) { q[i].opt = Read(), q[i].x = Read(); if (q[i].opt != 2) q[i].y = Read(); }
	for (int i = 1; i <= m; ++i)
	{
		if (q[i].opt != 2) addEdge(i - 1, i);
		else addEdge(q[i].x, i);
	}
	for (int i = 1; i <= n; ++i) fa[i] = i, Size[i] = 1, ++f[a[i].id][(i - 1) / Block + 1];
	dfs(0);
	for (int i = 1; i <= m; ++i)
		if (q[i].opt == 3) printf("%d\n", ans[i]);
	return 0;
}

标签:int,题解,Ynoi2014,unsigned,P5064,fx,MAXN,fy,操作
来源: https://www.cnblogs.com/Plozia/p/16463637.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有