首页 > 其他分享 >【题解】P4768 [NOI2018] 归程 / Kruskal 重构树

【题解】P4768 [NOI2018] 归程 / Kruskal 重构树

时间:2023-11-13 23:35:51浏览次数:39  
标签:重构 结点 pq P4768 题解 Kruskal int maxn dis

补补以前懒得总结的零碎东西。

kruskal 重构树

使用条件:求无向图中两点之间所有路径的最大边权的最小值

构造:

  1. 依 kruskal 得到最小生成树

  2. 从小到大考虑生成树中的边 \((u, v)\)

  3. 对于 \((u, v)\),新建一个结点,作为重构树中 \(u, v\) 的父结点

  4. 该结点的点权为 \((u, v)\) 的边权

性质:

  1. 原图中两点之间所有路径的最大边权的最小值

    = 最小生成树上两点之间路径的边权最大值

    = 重构树两点 lca 的点权

  2. kruskal 重构树是一棵二叉树

  3. kruskal 重构树以最小生成树构造时为大根堆,反之同理

  4. 令 \(v\) 该结点为重构树上 \(u\) 深度最浅的点权不超过 \(w\) 的祖先结点,则原图中结点 \(u\) 经过边权不超过 \(w\) 的边能到达的结点为 \(v\) 的子树

求最小边权的最大值同理。

思路

Kruskal 重构树。

首先意识到只经过海拔不超过定值的点可以通过 Kruskal 重构树的子树表示,答案即为子树中任意一个结点到 \(1\) 号结点经过的积水边总长的最小值。

发现其实等价于子树中结点到 \(1\) 号结点的最短路,原因考虑答案路径上最后一个非积水边的终点在所求子树内。

求子树的根结点可以考虑树上倍增。

单次时间复杂度 \(O(n \log n)\).

代码

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

#define il inline

const int maxn = 2e5 + 5;
const int maxm = 4e5 + 5;
const int maxt = maxn << 1;
const int lg_sz = 21;

struct edg
{
	int u, v, l, a;
	
	bool operator < (const edg& rhs) const { return (a > rhs.a); }
} edge[maxm];

int T, n, m, q, k, s;
bool vis[maxn];
int dis[maxn];
vector<int> g[maxn], w[maxn];

namespace DSU
{
	int fa[maxt];
	
	void dsu_init(int n) { for (int i = 1; i <= n; i++) fa[i] = i; }
	
	int get(int x) { return (fa[x] == x ? x : (fa[x] = get(fa[x]))); }
}
using namespace DSU;

namespace tree
{
	int nd = 0;
	int anc[maxt][lg_sz], val[maxt];
	int dp[maxt];
	vector<int> tr[maxt];
	
	void dfs(int u)
	{
		dp[u] = (u <= n ? dis[u] : 1061109567);
		for (int v : tr[u]) dfs(v), dp[u] = min(dp[u], dp[v]), anc[v][0] = u;
	}
	
	void calc()
	{
		dfs(nd);
		for (int j = 1; (1 << j) <= nd; j++)
			for (int i = 1; i <= nd; i++)
				anc[i][j] = anc[anc[i][j - 1]][j - 1];
	}
	
	int query(int u, int lim)
	{
		for (int i = lg_sz - 1; i >= 0; i--)
			if (anc[u][i] && (val[anc[u][i]] > lim)) u = anc[u][i];
		return dp[u];
	}
}
using namespace tree;

void init()
{
	for (int i = 1; i <= nd; i++) tr[i].clear();
	for (int i = 1; i <= n; i++) g[i].clear(), w[i].clear();
}

void dijkstra()
{
	priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > pq;
	memset(vis, false, (n + 1) * sizeof(bool));
	memset(dis, 0x3f, (n + 1) * sizeof(int)); dis[1] = 0;
	pq.push(make_pair(0, 1));
	while (!pq.empty())
	{
		int u = pq.top().second; pq.pop();
		if (vis[u]) continue;
		vis[u] = true;
		for (int i = 0, v, len; i < g[u].size(); i++)
		{
			v = g[u][i], len = w[u][i];
			if (dis[v] > dis[u] + len)
			{
				dis[v] = dis[u] + len;
				pq.push(make_pair(dis[v], v));
			}
		}
	}
}

void kruskal()
{
	sort(edge + 1, edge + m + 1);
	dsu_init(n), nd = n;
	for (int i = 1; i <= m; i++)
	{
		int fu = get(edge[i].u), fv = get(edge[i].v);
		if (fu != fv)
		{
			nd++, fa[nd] = fa[fu] = fa[fv] = nd, val[nd] = edge[i].a;
			tr[nd].push_back(fu), tr[nd].push_back(fv);
		}
	}
}

void solve()
{
	scanf("%d%d", &n, &m);
	init();
	for (int i = 1, u, v, l, a; i <= m; i++)
	{
		scanf("%d%d%d%d", &u, &v, &l, &a);
		edge[i] = (edg){u, v, l, a};
		g[u].push_back(v), w[u].push_back(l);
		g[v].push_back(u), w[v].push_back(l);
	}
	dijkstra(), kruskal(), calc();
	scanf("%d%d%d", &q, &k, &s);
	int last_ans = 0;
	while (q--)
	{
		int v, p;
		scanf("%d%d", &v, &p);
		v = (v + k * last_ans - 1) % n + 1;
		p = (p + k * last_ans) % (s + 1);
		printf("%d\n", last_ans = query(v, p));
	}
}

int main()
{
	scanf("%d", &T);
	while (T--) solve();
	return 0;
}

标签:重构,结点,pq,P4768,题解,Kruskal,int,maxn,dis
From: https://www.cnblogs.com/lingspace/p/p4768.html

相关文章

  • SPOJ1805 HISTOGRA - Largest Rectangle in a Histogram 题解
    LinkSPOJ1805HISTOGRA-LargestRectangleinaHistogramQuestion在一条水平线上有\(n\)个高为\(a_i\)的矩形,求包含于这些矩形的最大子矩形面积。Solution我们定义\(L_i\)表示有\(a_i\)这个高度的一根悬线,往左最多能平移到什么位置初始化显然,\(a_i=i\)考虑转移......
  • 题解 AT_codefestival_2016_final_f【Road of the King】
    注意到当前移动到的位置并不重要,重要的是经过的点数和\(1\)所在强连通分量大小,因此把它们放进状态里:设\(f_{i,j,k}\)表示进行\(i\)次移动,经过了\(j\)个不同的点,此时\(1\)所在的强连通分量大小为\(k\)的方案数。考察下一次移动到的点的情况:没有访问过:共有\(n-j\)......
  • UVA11282 题解
    题意简述Kelly寄出去\(n\)封邀请函,但她希望只有小于等于\(m\)个人收到他们自己的邀请函(即有至少\(n-m\)个人收到了别人的邀请函)。思路形成容易发现,这道题是一个典型的错排题,我们只需要分别求出\(n-m\)个元素到\(n\)个元素的错排即可。接下来为错排的推导,我们令\(f......
  • [题解] P4755 Beautiful Pair
    P4755BeautifulPair给你一个长度为\(n\)的序列\(a\),求有多少个区间\([l,r]\)满足\(a_l\cdota_r\le\max_{i=l}^ra_i\)。\(n\le10^5,a_i\le10^9\)。首先按最大值位置分治。记当前分治区间为\([l,r]\),分治中心为\(mid\)。那么我们现在要做的就是统计跨......
  • 【题解 P4062 & P8313】 Yazid 的新生舞会&Izbori
    [COCI2021-2022#4]Izbori题目描述Malnar先生正在竞选县长,这个县一共有\(n\)栋房屋,每栋房屋里都住着一位居民。Malnar先生知道,选举的赢家不一定是最好的候选人,而是在选举前举办的宴会最好的候选人。因此,在选举前几天,他将邀请第\(l\)至\(r(l\ler)\)栋房屋内居住的居民,为......
  • 【题解】CF1891E - Brukhovich and Exams
    【题解】CF1891E-BrukhovichandExamshttps://www.luogu.com.cn/problem/CF1891E我们考虑把区间分段:若两个相邻的数不互素,中间分开;若两个相邻的数中有且仅有一个\(1\),中间分开。那么我们得到了两种区间:全\(1\)区间与无\(1\)区间。若两个相邻数在同一区间内,那么就在区间......
  • [题解] CFgym101623F Factor-Free Tree
    Factor-FreeTree当一棵二叉树中的每个节点的权值都与它所有祖先的权值互质时,我们称它为factor-freetree。给你一棵按照中序遍历的顺序的权值序列\(a\),求这个序列是否对应这一棵factor-freetree。如果是就输出每个节点的父亲。\(n\le10^5,a_i\le10^7\)。考虑怎么......
  • SP2139题解
    思路这题数据范围小,暴力就可以了。首先我们用map来统计每个人的下标,用\(bk_{i,j}\)表示第\(i\)个人第\(j\)题是否知道答案。对于每次合作交流,暴力修改就可以了,先统计出两个人的下标,假设一个为\(x\),另一个为\(y\)。然后,如果\(bk_{x,i}\)和\(bk_{y,i}\)中(\(1\lei......
  • [ARC106E] Medals 题解
    题意有一个商店和\(N\)名员工,其中第\(i\)名员工在第\(1\simA_i\)天工作,在第\(A_i+1\sim2\timesA_i\)休息,接下来每\(A_i\)天改变一次状态。每一天你都可以选择一名来上班的员工并为其颁一个奖,求使得每名员工都获得至少\(K\)个奖的最小天数。\(1\leN\le......
  • [题解] CF1156E Special Segments of Permutation
    SpecialSegmentsofPermutation给你一个排列\(p\),求有多少个区间\([l,r]\)满足\(p_l+p_r=\max_{i\in[l,r]}p_i\)。\(n\le2\times10^5\)。按最大值分治,记当前的分治中心为\(mid\),分治区间为\([l,r]\)。然后需要计算跨分治中心的贡献。如果\(mid-l......