首页 > 其他分享 >P4768 [NOI2018] 归程 题解

P4768 [NOI2018] 归程 题解

时间:2023-02-24 13:59:10浏览次数:40  
标签:重构 return P4768 题解 int text include NOI2018 节点

这是一个悲伤的题目,自这道题后,\(\text{Noi}\) 再无 \(\text{SPFA}\)。

首先讲一下重构树是啥。

重构树是基于 \(\text{kurskal 生成树}\) 算法的一棵树,对于每一次合并一条边,用一个新的节点,连接边的两个端点连起来,用新的点替换这两个点进行下次合并即可,新的点点权为合并的边权。(画个图即可)

重构树有一个特点,即是原图中两个点间的所有路径中每个路径的边权最小的最大(最大的最小)即是两个点在重构树的 \(\text{LCA}\)。(画图解决)

所以本题怎么去使用重构树呢。

首先对于海拔,我们先建一棵最大生成树,并建重构树,可以证明,重构树中深度越小,海拔越小。

那么对于一个询问的点 \(v\) 的祖先 \(t\),如果 \(t\) 的海拔大于水位线,且 \(t\) 的父亲海拔小于等于水位线,那么在 \(t\) 以下的所有点都是可以开车达到的。

所以我们先从 \(1\) 节点开始跑一次最短路,对于每一次询问,找到重构树中规定的父亲,这个步骤可以提前用倍增优化,最后找到这个节点内所有子节点中与 \(1\) 距离最短的节点,选择从这个节点开始步行,最短的节点可以搜索一遍重构树进行处理。

然后如果没怎么弄懂可以看看代码。

时间复杂度:\(O((m+q)\times\log(n))\)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define int long long
using namespace std;
const int N=8e5+5;
int n,m,qs,cnt,t[N],dis[N],p[N][25],g[N][25],f[N];
struct node3
{
	int name,data;
};
priority_queue<node3>q;
bool operator <(node3 fi,node3 se)
{
	return fi.data>se.data;
}
struct node
{
	int from,to,data1,data2;
}b[N];
int cmp(node fi,node se)
{
	return fi.data2>se.data2;
}
struct node2
{
	int to,data1,data2;
};
vector<node2>a[N];
vector<int>c[N];
int afind(int x)
{
	if(x==f[x])return x;
	return f[x]=afind(f[x]);
}
void krus()
{
	cnt=n;
	for(int i=1;i<=2*n;i++)f[i]=i;
	sort(b+1,b+1+m,cmp);
	for(int i=1;i<=m;i++)
	{
		int u=b[i].from,v=b[i].to,w2=b[i].data2;
		if(afind(u)==afind(v))continue;
		cnt++;
		dis[cnt]=min(dis[afind(u)],dis[afind(v)]);
		c[cnt].push_back(afind(u));
		c[cnt].push_back(afind(v));
		f[afind(u)]=f[afind(v)]=cnt;
		t[cnt]=w2;
	}
}
void dijstra()
{
	for(int i=2;i<=n;i++)dis[i]=1e18;
	q.push({1,0});
	while(!q.empty())
	{
		int x=q.top().name;
		q.pop();
		int len=a[x].size();
		for(int i=0;i<len;i++)
		{
			if(a[x][i].data1+dis[x]<dis[a[x][i].to])
			{
				dis[a[x][i].to]=dis[x]+a[x][i].data1;
				q.push({a[x][i].to,dis[a[x][i].to]});
			}
		}
	}
}
void dfs(int x,int fa)
{
	int len=c[x].size();
	p[x][0]=fa;
	g[x][0]=t[fa];
	for(int i=1;i<=20;i++)p[x][i]=p[p[x][i-1]][i-1],g[x][i]=t[p[x][i]];
	for(int i=0;i<len;i++)dfs(c[x][i],x);
}
int findans(int x,int y)
{
	for(int i=20;i>=0;i--)if(g[x][i]>y)x=p[x][i];
	return x;
}
signed main()
{
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		for(int i=1;i<=2*n;i++)a[i].clear(),c[i].clear();
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int u,v,dat1,dat2;
			scanf("%lld%lld%lld%lld",&u,&v,&dat1,&dat2);
			a[u].push_back({v,dat1,dat2});
			a[v].push_back({u,dat1,dat2});
			b[i]={u,v,dat1,dat2};
		}
		dijstra();
		krus();
		dfs(cnt,0);
		int k,s;
		scanf("%lld%lld%lld",&qs,&k,&s);
		int lastans=0;
		for(int i=1;i<=qs;i++)
		{
			int x,y;
			scanf("%lld%lld",&x,&y);
			x=(x+k*lastans-1)%n+1;
			y=(y+k*lastans)%(s+1);
			int ans=dis[findans(x,y)];
			printf("%lld\n",ans);
			lastans=ans;
		}
	}
	return 0;
}
/*
1
4 3
1 2 50 1
2 3 100 2
3 4 50 1
5 0 2
3 0
2 1
4 1
3 1
3 2
*/

标签:重构,return,P4768,题解,int,text,include,NOI2018,节点
From: https://www.cnblogs.com/gmtfff/p/p4768.html

相关文章

  • P3247 [HNOI2016]最小公倍数 题解
    题意简述:给定一个无向图,边权带两个值\((a,b)\),给定\(q\)次询问,每次询问给定两个点,求两个点直接是否有\(\max(a)=x\)且\(\max(b)=y\)的简单或非简单路径。解:如果......
  • P2489 [SDOI2011]迷宫探险 题解
    题意简述:一个\(n\timesm\)的带墙体单入口多出口迷宫中有\(k\)个陷阱,陷阱分为有害或无害,有害会使人掉血,给出所有垃圾的有害与无害的所有排列组成的概率,给定人的血量,求......
  • P4416 [COCI2017-2018#1] Plahte 题解
    题意简述:给定\(n\)个互不相交,可以重叠的矩阵,对某些点染色,这个点上的所有矩阵会被染上这个颜色,求最后每个矩阵会有多少种颜色。解:我们可以先把矩阵拆成上下两条水平线......
  • CF1034A Enlarge GCD 题解
    题意简述:删去最少的数使所有的数的\(\text{gcd}\)增加。解:先对每个数除以所有数的\(\text{gcd}\),然后剩下的需要找到所有数的质因子,找到一个最多的序列中数拥有的质......
  • CF436E Cardboard Box 题解
    一道经典的反悔贪心题。考虑每次选择使总星数加一,那么总共有四种情况。一、对于一关没有星,选一颗星,代价为\(a_i\)。二、对于一关有一颗星,再选一颗星,代价为\(b_i-a_i\)......
  • CF204E Little Elephant and Strings 题解
    由于是多个串,还与每个子串的信息有关,很容易想到用SA或广义SAM。这里选择用SA。首先先把字符串转化为数组,连接起来,中间用一些不会出现的数。处理出后缀数组与\(height......
  • P5842 [SCOI2012]Blinker 的仰慕者 题解
    题意简述:求\([l,r]\)内各个数位积为\(k\)的数的和。解:在看题解前,请先确认自己是否精通了数位dp模板题,否则会很难理解。对于朴素的数位dp,我们可以记录\(f_{i,j......
  • P5416 [CTSC2016]时空旅行 题解
    首先题中的\(y,z\)好像没啥用……首先对于每一次询问,要求\(\min((x_0-x_i)^2+c_i)\)设\((x_0-x_i)^2+c_i=ans\)。那么\(x_0^2+x_i^2-2x_0x_i+c_i=ans\)所以\(x_0......
  • P7862 [COCI2015-2016#2] DRŽAVA 题解
    适当进行骗分是真的有用。\(40pts\):对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。设\(dp_{i,j}\)代表以\(i\)为编号的一......
  • P1379 八数码难题 题解
    IDA*练习题由于题目问最小步数,很好想到可以用迭代式加深搜索,或是广搜,这里用的是深搜。枚举每次搜索的深度,也就是移动的步数,然后正常深搜,若达到目标解,返回\(\text{ture}......