首页 > 其他分享 >【题解】P4768 [NOI2018] 归程(树上倍增,Kruskal 重构树,dijkstra)

【题解】P4768 [NOI2018] 归程(树上倍增,Kruskal 重构树,dijkstra)

时间:2023-01-17 22:44:59浏览次数:71  
标签:子树内 fa P4768 题解 Kruskal int now 边权

【题解】P4768 [NOI2018] 归程

作为一道 NOI 的 D1T1,放在现在或许难度不够(?),但可能在 Kruskal 重构树还未普及的当时,还是相对来说比较难的一道题吧。

写篇题解记录一下。


题目链接

P4768 [NOI2018] 归程

题意概述

给定一张 \(n\) 个点 \(m\) 条边的无向图,对于每条边有两个参数:长度 \(l\) 和海拔 \(a\)。

有 \(Q\) 组询问,每组询问给定 \(v,p\),表示每一天以 \(v\) 作为起点,\(1\) 作为终点,问从走到的第一条海拔 \(\le p\) 的边开始,在它及它之后走到的所有边的边权之和最小是多少。

本题多测。

思路分析

考虑将每一天的所有路程分为两部分:先从 \(v\) 走到最后一条海拔 \(> p\) 的边 \(u\),再从 \(u\) 走到 \(1\)。

题目要求的就是从 \(u\) 到 \(1\) 的边权之和最小是多少。

我们首先思考这里的 \(u\) 需要满足什么条件。

有以下两点:

  • \(v\) 不经过海拔 \(\le p\) 的边能够到达 \(u\);
  • \(u\) 到 \(1\) 的边权和最小。

对于第一点,可以发现它其实是一个比较板的 Kruskal 重构树的模型。

由于是“不经过海拔 \(\le p\) 的边”,那么实际上就相当于“经过的边只有海拔 \(>p\) 的”。

以海拔为边权建图,问题就转化为,从 \(v\) 开始,只经过边权 \(>p\) 的边能够到达哪些点。

显然我们可以直接将边权从大到小排序,然后建立 Kruskal 重构树。

若不考虑边权 \(>p\) 的限制,那么对于一个点 \(u\) 能够到达的点就是和它在一个联通块内的所有点。

再考虑边权的限制,根据 Kruskal 的性质,从叶子节点到根节点一路往上走,点权一定是递减的。

那么根据这个性质,从一个点 \(u\) 出发能够到达的边权 \(>p\) 的点一定是从 \(u\) 到根节点的路径上的某个点 \(x\) 的子树内所有的点。且这个 \(x\) 的点权一定是从 \(u\) 走到根节点的路径上,最后一个点权 \(>p\) 的。

换句话说,假如从 \(u\) 到根节点的路径上,\(x\) 的点权 \(>p\),且 \(x\) 之后点的点权都 \(\le p\),那么 \(x\) 的子树内的所有点就是符合题意的点。

这一点很容易证明:

  • 首先证明 \(x\) 子树内的所有点一定满足题意。这个根据 Kruskal 的性质,即若 \(x\) 的子树中存在两点 \(u,v\),那么实际的图上 \(u\) 到 \(v\) 的路径上最小边权的最大值一定等于 \(x\) 的点权。所以 \(x\) 的点权是其子树中任意两点的路径上的最小边权最大值,那么既然最小边权都 \(>p\) 了,那么其它的也一定 \(>p\),所以满足题意;
  • 再证明从 \(u\) 到根节点的路径上的其它点都不满足题意。
    • 对于 \(x\) 之前的点,显然 \(x\) 的子树完全包含它之前的点,那么显然将答案设在 \(x\) 之前的点会漏掉一些点,所以不优。
    • 对于 \(x\) 之后的点,由于其点权一定 \(\le p\),那么其子树内的任意两点的最小边权最大值一定 \(\le p\),不符合要求边权 \(>p\) 的限制,所以只有 \(x\) 子树内的点满足条件。

那么证明了答案在 \(x\),那么怎么找到 \(x\) 呢。其实可以直接树上倍增。

预处理出来一个数组 \(fa_{x,i}\) 表示从 \(x\) 向上走 \(2^i\) 步能够到哪。然后我们往上倍增的跳,假设当前在 \(now\),只要 \(fa_{now,i}\) 的点权 \(>p\) 就跳,反之不跳。即可找到满足条件的 \(x\)。

我们找到了 \(x\),相当于只完成了第一点。

即符合第一个条件的点一定在 \(x\) 的子树内。考虑第二点,满足条件的 \(u\) 就是 \(x\) 子树内到 \(1\) 的最短路最小的点。

从一个点到 \(1\) 的最短路可以直接建立反图 dijkstra 求出。

而如何判断 \(x\) 子树内哪个点距离 \(1\) 最近呢?

直接暴力枚举吗?显然时间复杂度炸了。

实际上我们可以直接处理出来一个数组 \(mi_{now}\) 表示 \(now\) 所在子树内距离 \(1\) 最近的点的距离是多少。

由于重构树是个二叉树,那么可以直接用类似线段树 pushup 的操作求,即 \(mi_{now}=\min\{mi_{now},mi_{ls},mi_{rs}\}\)。

总复杂度 \(O(T n\log n)\)。

易错点

  • 注意多测清空;
  • 注意有些数组要开二倍空间;
  • 注意重构树的边比原树多了 \(n-1\) 条;
  • 注意强制在线 \(lastans\) 变量的更新;

代码实现

//luoguP4768
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define int long long
using namespace std;
const int maxn=2e5+10;
int fa[maxn<<1],vis[maxn<<1],dis[maxn],sum[maxn<<1],val[maxn<<1];
int mi[maxn<<1][25],f[maxn<<1][25],dep[maxn<<1];
int n,m,tot;

struct nod{int u,v,w;}e[maxn<<1];

struct Node{
	int v,w;
	bool operator<(const Node &t)const
	{
		return w>t.w;
	}
};

basic_string<Node>edge[maxn];
basic_string<int>edge2[maxn<<1];

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*10+ch-48;ch=getchar();}
	return x*f;
}

int cmp(nod a,nod b){return a.w>b.w;}

int find(int x)
{
	if(x!=fa[x])fa[x]=find(fa[x]);
	return fa[x];
}

void add(int x,int y)
{
	x=find(x);y=find(y);
	fa[x]=y;
}

void dfs(int now)
{
	for(int nxt:edge2[now])
	{
		dfs(nxt);
		sum[now]=min(sum[now],sum[nxt]);
	}
}

void Kruskal()
{
	for(int i=1;i<=(n<<1);i++)fa[i]=i;
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		if(find(e[i].u)==find(e[i].v))continue;
		int x=find(e[i].u),y=find(e[i].v);
		add(e[i].u,e[i].v);
		val[++tot]=e[i].w;
		fa[x]=fa[y]=fa[tot]=tot;
		edge2[tot]+=x;edge2[tot]+=y;
	}
}

void dijkstra(int s)
{
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)dis[i]=(1ll<<60);
	dis[s]=0;
	priority_queue<Node>q;
	q.push(Node{s,dis[s]});
	while(!q.empty())
	{
		Node now=q.top();
		q.pop();
		if(vis[now.v])continue;
		vis[now.v]++;
		for(Node y:edge[now.v])
		{
			if(dis[y.v]>dis[now.v]+y.w)
			{
				dis[y.v]=dis[now.v]+y.w;
				q.push(Node{y.v,dis[y.v]});
			}
		}
	}
	for(int i=1;i<=n;i++)sum[i]=dis[i];
}

void dfs2(int x,int fath)
{
	dep[x]=dep[fath]+1;
	mi[x][0]=val[fa[x]];
	f[x][0]=fath;
	for(int i=1;i<=20;i++)
	{
		f[x][i]=f[f[x][i-1]][i-1];
		mi[x][i]=min(mi[x][i],mi[f[x][i-1]][i-1]);
	}
	for(int y:edge2[x])
	{
		if(y==fath)continue;
		dfs2(y,x);
	}
}

signed main()
{
	int T=read();
	while(T--)
	{
		for(int i=1;i<=n;i++)edge[i].clear();
		for(int i=1;i<=(n<<1);i++)edge2[i].clear();
		memset(dep,0,sizeof(dep));
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			int u,v,l,a;
			u=read();v=read();l=read();a=read();
			e[i]=nod{u,v,a};
			edge[u]+=Node{v,l};
			edge[v]+=Node{u,l};
		}
		dijkstra(1);
		tot=n;
		Kruskal();
		for(int i=n+1;i<=(n<<1);i++)sum[i]=(1ll<<60);
		dfs(tot);
		dfs2(tot,0);
		int q=read(),k=read(),s=read();
		int lastans=0;
		while(q--)
		{
			int v0=read(),p0=read();
			int v=(v0+k*lastans-1)%n+1;
			int p=(p0+k*lastans)%(s+1);
			int now=v;
			for(int i=20;i>=0;i--)
			{
				int cha=dep[now]-dep[tot];
				if((cha>=(1<<i))&&val[f[now][i]]>p)
				{
					now=f[now][i];
				}
			}
			cout<<sum[now]<<'\n';
			lastans=sum[now];
		}
	}
	return 0;
}

标签:子树内,fa,P4768,题解,Kruskal,int,now,边权
From: https://www.cnblogs.com/xrkforces/p/luogu-P4768.html

相关文章

  • P5020 [NOIP2018 提高组] 货币系统 题解
    注意:此题题解写的较为简略。P5020[NOIP2018提高组]货币系统转化为完全背包即可。#include<iostream>#include<cstring>#include<algorithm>usingnamespaces......
  • P1941 [NOIP2014 提高组] 飞扬的小鸟 题解
    WC-2023上的题目。线性动态规划P1941[NOIP2014提高组]飞扬的小鸟我们先不管障碍物。设\(f[i][j]\)表示来到点\((i,j)\)的最少点击屏幕数。因为每秒要不上升\(......
  • P1880 [NOI1995] 石子合并 题解
    P1880[NOI1995]石子合并区间DP。首先将其复制一遍(因为是环)。设\(f[i][j]\)表示将\(i\)到\(j\)段的石子合并需要的次数。有\[f[i][j]=0(i=j)\]\[f[i][j]......
  • 涂满它!(涂色问题 (原题:水叮当的舞步))题解
    F.涂满它!内存限制:256MiB时间限制:1000ms标准输入输出题目类型:传统评测方式:文本比较题目描述Flood-it是谷歌+平台上的非常好玩的一款游戏,游戏界面如下所示:在......
  • 2023牛客寒假算法基础集训营1题解
    写在前面全文收集了部分学长以及我自己的代码,本蒟蒻第一次写博客,效果不好请见谅TwT原题链接:https://ac.nowcoder.com/acm/contest/46800#questionA:WorldFinal?WorldC......
  • 洛谷普及组模拟赛 题解报告
    洛谷普及组模拟赛题解报告\[\bf{Prepared\by\InoueTakina.}\]前言:祝大家身体健康。本场比赛较为良心,经过了多次难度平衡,应该严格低于NOIP2018提高组,相信大家......
  • 洛谷P3195 玩具装箱 题解报告
    题目地址题意:如题所述。分析:斜率优化dp模板题。题目没看清就下手,自以为题面所述中i>j;原始dp式子也没设计准确。中途在错误方向上头铁时,甚至没注意到横坐标是沿......
  • 洛谷P3628 特别行动队 题解报告
    题目地址题意:把正整数序列分隔为若干区间,若单个区间的元素之和为X,则其贡献为\(aX^2+bX+c\)。求所有区间的贡献之和的最大值。分析:斜率优化dp模板题。这篇博客描述得......
  • 1.17模拟赛题解
    T1设\(dp_{i,j}\)前\(i+j\)个人站队,第一排站\(i\)个人的方案数。每次对相同身高的一段人进行转移。暴力复杂度是正确的。时间复杂度\(O(n^2)\)。T4考虑二分答......
  • iSCSI的客户端messages频繁报错问题解决
    问题现象:在自己的工作站中安装的RAC测试环境,使用了iSCSI模拟共享存储,环境运行OK,但是在messages信息中频繁报错如下:[root@db01rac2~]#tail-20f/var/log/messagesJan......