NOIP2023模拟7联测28 距离

2023-11-01 09:52
文章标签 模拟 距离 28 联测 noip2023

本文主要是介绍NOIP2023模拟7联测28 距离,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目大意

给一棵 n n n个节点的无根树,每条边都有一个边权 w i w_i wi。令 d i s ( x , y ) dis(x,y) dis(x,y)为点 x x x到点 y y y的距离。

你需要维护一个初始为空的点对集合 S S S,有 m m m次操作,每次操作有两种类型:

  • 1 a b S S S中插入点对 ( a , b ) (a,b) (a,b)
  • 2 x y查询下面的式子并输出结果
    min ⁡ ( a , b ) ∈ S d i s ( x , a ) + d i s ( y , b ) \min\limits_{(a,b)\in S}dis(x,a)+dis(y,b) (a,b)Smindis(x,a)+dis(y,b)

若查询时 S S S为空,则输出 − 1 -1 1

1 ≤ n , m ≤ 2 × 1 0 5 , 1 ≤ w i ≤ 1 0 9 1\leq n,m\leq 2\times 10^5,1\leq w_i\leq 10^9 1n,m2×105,1wi109

在子任务 2 2 2中, b = 1 b=1 b=1

时间限制 4000 m s 4000ms 4000ms,空间限制 512 M B 512MB 512MB


题解

首先,我们考虑子任务 2 2 2,此时相当于点对变成了但单个点。问题就变成了每次往点集插入一个点,或者询问距离某个点最近的点的距离。

考虑用点分树,在每个重心维护其点分树内距离重心最近的点即可,修改和查询时不断往上跳即可。时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

下面考虑如何处理点对的情况。

我们可以将所有操作都离线下来,将 a i a_i ai x i x_i xi进行点分治,在每一层点分治中处理所有 a i a_i ai x i x_i xi在其点分治子树中的操作。设当前的分治中心为点 u u u,则按时间顺序在另一棵点分树上插入 b i b_i bi,并加上 d i s ( u , a i ) dis(u,a_i) dis(u,ai),查询时就使用使用普通点分树的操作查询即可(也就是不断往上跳)。

每次修改和查询会在点分治中的 O ( log ⁡ n ) O(\log n) O(logn)个点中体现,每次体现都要在点分树上 O ( log ⁡ n ) O(\log n) O(logn)来进行修改和查询,所以总时间复杂度为 O ( m log ⁡ 2 n ) O(m\log^2n) O(mlog2n)

可以参考代码帮助理解。

code

#include<bits/stdc++.h>
using namespace std;
const int N=200000;
int n,m,fl=0,root,rt=0,tot=0,d[2*N+5],l[2*N+5],r[N+5];
int siz[N+5],mxd[N+5],z[N+5],fa[N+5],dw[N+5];
long long w[2*N+5],mn[N+5],ans[N+5],dis[N+5][20];
struct que{int op,x,y;
}q[N+5];
vector<int>G[N+5],id[N+5];
void add(int xx,int yy,int zz){l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;w[tot]=zz;
}
void dfs(int u,int fa,int all,int dw){siz[u]=1;mxd[u]=0;mn[u]=1e18;for(int i=r[u];i;i=l[i]){if(d[i]==fa||z[d[i]]) continue;dis[d[i]][dw]=dis[u][dw]+w[i];dfs(d[i],u,all,dw);siz[u]+=siz[d[i]];mxd[u]=max(mxd[u],siz[d[i]]);}mxd[u]=max(mxd[u],all-siz[u]);if(mxd[u]<mxd[rt]) rt=u;
}
void dd(int u){z[u]=1;for(int i=r[u];i;i=l[i]){if(z[d[i]]) continue;rt=0;dis[d[i]][dw[u]]=w[i];dfs(d[i],0,siz[d[i]],dw[u]);G[u].push_back(rt);fa[rt]=u;dw[rt]=dw[u]+1;dd(rt);}
}
void pt(int x,long long v){for(int tx=x;tx;tx=fa[tx]){mn[tx]=min(mn[tx],v+dis[x][dw[tx]]);}
}
long long gt(int x){long long re=1e18;for(int tx=x;tx;tx=fa[tx]){re=min(re,mn[tx]+dis[x][dw[tx]]);}return re;
}
void clr(int x){for(int tx=x;tx;tx=fa[tx]) mn[tx]=1e18;
}
void solve(int u){for(int i:id[u]){if(q[i].op==1) pt(q[i].y,dis[q[i].x][dw[u]]);else ans[i]=min(ans[i],gt(q[i].y)+dis[q[i].x][dw[u]]);}for(int i:id[u]){if(q[i].op==1) clr(q[i].y);}for(int v:G[u]) solve(v);
}
int main()
{freopen("distance.in","r",stdin);freopen("distance.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1,x,y,z;i<n;i++){scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);}for(int i=1;i<=m;i++){scanf("%d%d%d",&q[i].op,&q[i].x,&q[i].y);ans[i]=1e18;}mxd[0]=1e9;dfs(1,0,n,0);root=rt;dw[root]=1;dd(root);for(int i=1;i<=m;i++){for(int u=q[i].x;u;u=fa[u]) id[u].push_back(i);}solve(root);for(int i=1;i<=m;i++){if(q[i].op==1) fl=1;else{if(!fl) printf("-1\n");else printf("%lld\n",ans[i]);}}return 0;
}

这篇关于NOIP2023模拟7联测28 距离的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/322173

相关文章

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码  2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法,这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列 算法分析 这道题其实就是要将连续且相同的字符替换成字符重复的次数+

模拟实现vector中的常见接口

insert void insert(iterator pos, const T& x){if (_finish == _endofstorage){int n = pos - _start;size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;reserve(newcapacity);pos = _start + n;//防止迭代

PHP实现二叉树遍历(非递归方式,栈模拟实现)

二叉树定义是这样的:一棵非空的二叉树由根结点及左、右子树这三个基本部分组成,根据节点的访问位置不同有三种遍历方式: ① NLR:前序遍历(PreorderTraversal亦称(先序遍历)) ——访问结点的操作发生在遍历其左右子树之前。 ② LNR:中序遍历(InorderTraversal) ——访问结点的操作发生在遍历其左右子树之中(间)。 ③ LRN:后序遍历(PostorderT

【vue3|第28期】 Vue3 + Vue Router:探索路由重定向的使用与作用

日期:2024年9月8日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉在这里插入代码片得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083;0.98365 = 0.0006 说

1 模拟——67. 二进制求和

1 模拟 67. 二进制求和 给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。 示例 1:输入:a = "11", b = "1"输出:"100"示例 2:输入:a = "1010", b = "1011"输出:"10101" 算法设计 可以从低位到高位(从后向前)计算,用一个变量carry记录进位,如果有字符没处理完或者有进位,则循环处理。两个字符串对