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

P4768 [NOI2018] 归程

时间:2023-08-25 10:44:44浏览次数:39  
标签:800001 cur P4768 int edge dis NOI2018 节点 归程

链接:P4768 [NOI2018] 归程

观察一下题目,如果没有车,求一个单源最短路就行了(但不要使用一种广为人知的最短路算法)

现在考虑有车的情况,显然最优策略是坐车到离\(1\)号节点最近的车能去的点下车。于是我们还是可以预处理每个点到\(1\)号节点的最短路,每次从节点\(v\)开始广搜它能去的所有海拔大于\(p\)的节点,时间复杂度\(O(Qn)\),瓶颈在于广搜

在回顾一下,我们所求的是离\(1\)号节点最近的海拔大于一个值的点,完全不需要用广搜维护。如果是离线可以带权并查集,但是这题在线。有没有一种树形结构满足权值单调呢,并且子树内的点都可以不依靠子树外的节点联通——\(Kruskal\)重构树完美满足。

我们按照边的海拔从大到小排序,建树,用下面的树演示一下如何计算答案。

image

\(cur\)是出发节点\(v\)的一个祖先,如果\(cur\)的海拔大于\(p\),那么\(cur\)子树内的点都是联通的(车可以到达),因为权值单调,\(cur\)子树内的点肯定海拔也大于\(p\)。

贪心地,\(cur\)子树内的点肯定越多越好,也就是说\(cur\)是\(v\)的祖先里离根节点最近的海拔大于\(p\)的节点。怎么找\(cur\)?用倍增跳就行了(别忘了权值单调)。找到了\(cur\)以后,\(cur\)子树内最小\(dis\)值(预处理的最短路)就是答案

现在只剩最后一个问题,怎么找一个子树内最小的\(dis\)值?

这很简单,反正建完树以后树又不会变,预处理\(dfs\)一下就行了。

最短路\(O(mlongm)\),\(kruskal O(mlogm)\),倍增\(O(NlongN)\)。

ps:因为\(Kruskal\)重构树的节点数是\(N=2\times n+1\),所以不要忘了双倍空间

\(CODE\):

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t;
int nows;
int fa[800001];
int n,m;
int q,k,s;
int lastans;
struct stu{
	int from,to,val,h;
	bool operator<(const stu &kl)const{
		return h>kl.h;
	}
}edge[800001];

struct node{
	int x,dis;
	bool operator<(const node &kl)const{
		return dis>kl.dis;
	}
};

int tree[800001],dp[800001][30],dep[800001],dis[800001],ans[800001];
bool vis[800001];
vector<node> nbr[800001];
vector<int> line[800001];

int find(int xx){
	if(fa[xx]==xx) return xx;
	else return fa[xx]=find(fa[xx]);
}

void dijkstra(int s){
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	node cur={s,dis[s]};
	priority_queue<node> pq;
	pq.push(cur);
	while(!pq.empty()){
		cur=pq.top();
		pq.pop();
		int x=cur.x;
		if(vis[x]==true) continue;
		vis[x]=true;
		for(int i=0;i<nbr[x].size();i++){
			int y=nbr[x][i].x;
			int d=nbr[x][i].dis;
			if(dis[y]>dis[x]+d){
				dis[y]=dis[x]+d;
				node nxt={y,dis[y]};
				pq.push(nxt);
			}
		}
	}
	return ;
}

void kruskal(){
	sort(edge+1,edge+1+m);
	nows=n;
	for(int i=1;i<=m;i++){
		int x=find(edge[i].from);
		int y=find(edge[i].to);
		if(x!=y){
			nows++;
			tree[nows]=edge[i].h;
			fa[x]=fa[y]=fa[nows]=nows;
			line[nows].push_back(x);
			line[nows].push_back(y);
		}
	}
}

void dfs(int now){
	ans[now]=dis[now];
	for(int i=0;i<line[now].size();i++){
		dfs(line[now][i]);
		ans[now]=min(ans[now],ans[line[now][i]]);
	}
	return;
}

void get_lca(int now,int fa){
	dep[now]=dep[fa]+1;
	dp[now][0]=fa;
	for(int i=1;(1<<i)<=dep[now];i++){
		dp[now][i]=dp[dp[now][i-1]][i-1];
	}
	for(int i=0;i<line[now].size();i++){
		get_lca(line[now][i],now);
	}
	return;
}

int help(int now,int h){
	for(int i=23;i>=0;i--){
		if(dp[now][i] && tree[dp[now][i]]>h) now=dp[now][i];
	}
	return ans[now];
}

signed main(){
	cin>>t;
	while(t--){
		lastans=0;
		memset(tree,0xcf,sizeof(tree));
		memset(ans,0x3f,sizeof(ans));
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=n+m;i++){
			nbr[i].clear();
			line[i].clear();
		}
		cin>>n>>m;
		for(int i=1;i<=n;i++) fa[i]=i;
		for(int i=1;i<=m;i++){
			cin>>edge[i].from>>edge[i].to>>edge[i].val>>edge[i].h;
			node fi={edge[i].to,edge[i].val};
			nbr[edge[i].from].push_back(fi);
			fi={edge[i].from,edge[i].val};
			nbr[edge[i].to].push_back(fi);
		}
		dijkstra(1);
		kruskal();
		dfs(nows);
		get_lca(nows,0);
		cin>>q>>k>>s;
		while(q--){
			int v0,p0;
			cin>>v0>>p0;
			int v=(v0+k*lastans-1)%n+1;
			int p=(p0+lastans*k)%(s+1);
			//cout<<v<<" "<<p<<endl;
			lastans=help(v,p);
			cout<<lastans<<endl;
		}
	}
	return 0;
}

标签:800001,cur,P4768,int,edge,dis,NOI2018,节点,归程
From: https://www.cnblogs.com/wangwenhan/p/17656257.html

相关文章

  • P9507 [BalkanOI2018] Popa 题解
    原题传送门题目描述Ghiță有一个下标从\(0\)开始的正整数序列\(S\)。因为他是喀尔巴阡的国王,所以他想要构造一个节点编号为\(0,1,\ldots,N-1\)的二叉树,满足:树的中序遍历按节点编号升序排列。二叉树的中序遍历由以根的左子节点(如果存在)为根形成的子树的中序遍历,根的节......
  • [NOI2018] 你的名字
    题目描述小A被选为了ION2018的出题人,他精心准备了一道质量十分高的题目,且已经把除了题目命名以外的工作都做好了。由于ION已经举办了很多届,所以在题目命名上也是有规定的,ION命题手册规定:每年由命题委员会规定一个小写字母字符串,我们称之为那一年的命名串,要求每道题的名字......
  • [NOI2018] 屠龙勇士
    题意求解下列同余方程组,\[\begin{cases}b_1x\equiva_1\pmod{m_1}\\b_2x\equiva_2\pmod{m_2}\\\dots\\b_nx\equiva_n\pmod{m_n}\\\end{cases}\]其中,\(n\leqslant10^5,m_i\leqslant10^8,lcm(m_i)\leqslant10^{12},a_i\leqslant......
  • CF896E/洛谷 P4117 [Ynoi2018]五彩斑斓的世界/Welcome home, Chtholly
    分块。我们先来考虑修改对整块的影响。记值域为\(V=10^5\)。考虑对每一块维护\(V\)个集合\(S_1,S_2,\cdots,S_V\),第\(i\)个集合\(S_i\)维护了区间中所有\(=i\)的元素的一些信息,并维护区间的最大值\(m\),对于一次操作\(x\):若\(m\le2x\),我们暴力对每个\(i\in[x+1,......
  • [NOI2018] 你的名字
    给定串\(S,T_{1,\cdots,q}\),每次询问是\(S[l_i,r_i]\)的子串但不是\(T_i\)的子串的本质不同子串个数。\(|S|\le5e5,q\le1e5,\sum|T|\le1e6\)。我们先考虑\(l=1,r=|S|\)怎么做。显然我们使用SAM可以简单计算出\(T_i\)的本质不同子串数,那么我们肯定想算出来\(S\)......
  • [NOI2018] 归程 解题报告
    题面步行的最小距离很容易求,dij随便求一下每个点的最短路,然后找到与\(v\)能相互坐车到达的点,对这些点的最短路都有可能是答案,取个\(\min\)即可。所以本题最大的问题是怎么找到在水位线为\(p\)时,与\(v\)能相互坐车到达的点。可以想到只保留海拔大于\(p\)的边,因为只要考......
  • [Ynoi2018] 天降之物
    [Ynoi2018]天降之物这个根号分治太神啦。首先考虑一个朴素的暴力:对每个数维护出现位置的std::vector这样查询可以两个指针遍历std::vector做到平方复杂度。注意到复杂度和出现次数有关,那么就可以考虑阈值分治了,然而合并的操作使得我们不好维护信息。先考虑不带修的情况,对......
  • 【NOI2018】冒泡排序
    【NOI2018】冒泡排序Description最近,小S对冒泡排序产生了浓厚的兴趣。为了问题简单,小S只研究对\(1\)到\(n\)的排列的冒泡排序。下面是对冒泡排序的算法描述。......
  • P4768 [NOI2018] 归程 题解
    这是一个悲伤的题目,自这道题后,\(\text{Noi}\)再无\(\text{SPFA}\)。首先讲一下重构树是啥。重构树是基于\(\text{kurskal生成树}\)算法的一棵树,对于每一次合并一条......
  • 【题解】P4768 [NOI2018] 归程(树上倍增,Kruskal 重构树,dijkstra)
    【题解】P4768[NOI2018]归程作为一道NOI的D1T1,放在现在或许难度不够(?),但可能在Kruskal重构树还未普及的当时,还是相对来说比较难的一道题吧。写篇题解记录一下。题......