P9058 [Ynoi2004] rpmtdq 与 P9678 [ICPC2022 Jinan R] Tree Distance
思路:
注意到点对数量有 \(N^2\) 个,考虑丢掉一些无用的点对。
对于点对 \((x_1,y_1),(x_2,y_2)\),满足 \(x_1 \le x_2 < y_2 \le y_1\),即区间 \([x_2,y_2]\) 被 \([x_1,y_1]\) 包含,此时满足若询问到了 \([x_1,y_1]\),则一定会询问到 \([x_2,y_2]\)。
若满足 \(\operatorname{dis}(x_1,y_1) \ge \operatorname{dis}(x_2,y_2)\),那么此时可以将 \((x_1,y_1)\) 舍弃,因为若要用 \((x_1,y_1\)) 的贡献,不如直接去看 \((x_2,y_2)\) 的贡献,毕竟 \((x_1,y_1)\) 的贡献一定不会比 \((x_2,y_2)\) 更优。
那么我们可以定义若两个点对 \((x_1,y_1),(x_2,y_2)\) 满足以下条件,则称 \((x_1,y_1)\) 被 \((x_2,y_2)\) 支配:
\(x_1 \le x_2 < y_2 \le y_1\)。
\(\operatorname{dis}(x_1,y_1) \ge \operatorname{dis}(x_2,y_2)\)。
此时定义一个支配点对满足没有被任何一个点对支配,即我们需要找出所有的支配点对来计算贡献。
注意到是一个树上点对距离问题,考虑点分治解决。
令当前分治重心为 \(rt\),对于点 \(v\),令 \(S_v\) 表示当前联通块中所有满足 \(\operatorname{dis}(i,rt) \le \operatorname{dis}(v,rt)\) 的 \(i\) 组成的一个集合。
那么可以与 \(v\) 组成支配点对的点一定是 \(S_v\) 中 \(v\) 的前驱和后继,即 \(S_v\) 中 \(<v\) 中最大的数和 \(>v\) 中最小的数。
简单证一下,设 \(S_v\) 中 \(v\) 的前驱为 \(u\):
有 \(\operatorname{dis}(i,u) \le \operatorname{dis}(i,rt) + \operatorname{dis}(u,rt) \le \operatorname{dis}(i,rt) + \operatorname{dis}(v,rt) = \operatorname{dis}(i,v)\),即 \(\operatorname{dis}(i,u) \le \operatorname{dis}(i,v)\)。
注意到此时 \(i < u < v\) 或 \(u < v < i\),即 \((i,v)\) 被 \((i,u)\) 支配或 \((u,i)\) 被 \((v,i)\) 支配。
那么只有当 \(i=u\) 时,\((u,u)\) 点对不存在,\((u,v)\) 不会被其它 \(S_v\) 中的点对支配。
后继情况类似,就不多说了。
然后考虑如何快速找到支配点对,直接按照上面的方法找 \(S_v\),复杂度肯定是 \(O(N^2)\) 起步,考虑优化。
首先对于整个联通块的所有点,按照点的编号排序升序,然后维护一个 \(\operatorname{dis}(i,rt)\) 不降的单调栈。
那么有一个性质是,对于被点 \(i\) 弹出去的点 \(u\),肯定满足 \(i\) 是 \(u\) 后面第一个小于等于 \(\operatorname{dis}(u,rt)\) 的点且编号最小,即 \(i\) 是 \(S_u\) 中 \(u\) 的前驱;然后再倒着降序做一遍单调栈找后继即可。
此时我们来估算一下支配点对的数量,每个点最多被 \(\log N\) 个分治重心包含,每次包含最多增加 \(2\) 对支配点对,即总支配点对的数量为 \(N \log N\) 左右。
现在求出了全部的支配点对,即有贡献的点对,现在考虑如何求被一个区间包含的所有支配点对的最小贡献值,可以在线使用树套树,但是没必要。
考虑离线使用扫描线算法,因为树状数组不好维护后缀最值,考虑倒着扫左端点,然后对于每个点对,在左端点处将右端点贡献加入进去;那么对于一个在左端点的询问,就是一个前缀最小值。
时间复杂度为 \(O(N \log^2 N + Q \log N)\)。
完整代码:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
const ll N=2e5+10,M=1e6+10,INF=1e18;
bool Begin;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
int n,q;
ll ans[M];
vector<int> G[N];
vector<pair<int,ll>> E[N],Q[N];
void add(int u,int v,int w){
E[u].push_back({v,w});
E[v].push_back({u,w});
}
namespace Lowbit{
ll a[N];
inline void init(){
for(int i=1;i<=n;i++)
a[i]=INF;
}
inline void add(int x,ll w){
for(int i=x;i<=n;i+=lowbit(i))
a[i]=min(a[i],w);
}
inline ll query(int x){
ll ans=INF;
for(int i=x;i;i-=lowbit(i))
ans=min(ans,a[i]);
return ans;
}
};
namespace LCA{
int p[N],t[N],z[N],d[N],fa[N];
ll dep[N];
inline void dfs1(int u,int f){
p[u]=1;
for(auto t:E[u]){
int v=t.first,w=t.second;
if(v==f)
continue;
dep[v]=dep[u]+w;
d[v]=d[u]+1;
fa[v]=u;
dfs1(v,u);
p[u]+=p[v];
if(p[v]>p[z[u]])
z[u]=v;
}
}
inline void dfs2(int u,int k){
t[u]=k;
if(!z[u])
return ;
dfs2(z[u],k);
for(auto t:E[u]){
int v=t.first;
if(v==fa[u]||v==z[u])
continue;
dfs2(v,v);
}
}
inline int Lca(int u,int v){
while(t[u]!=t[v]){
if(d[t[u]]<d[t[v]])
swap(u,v);
u=fa[t[u]];
}
return d[u]<d[v]?u:v;
}
inline ll dis(int u,int v){
return dep[u]+dep[v]-(dep[Lca(u,v)]<<1ll);
}
inline void init(){
dfs1(1,1);
dfs2(1,1);
}
};
namespace Tree{
int sum,cnt,top,Max,root;
int T[N],siz[N];
pair<int,ll> dis[N];
bool del[N];
inline void add(int x,int y){
if(x>y)
swap(x,y);
G[x].push_back(y);
}
inline void getroot(int u,int fa){
int s=0;
siz[u]=1;
for(auto t:E[u]){
ll v=t.first;
if(del[v]||v==fa)
continue;
getroot(v,u);
siz[u]+=siz[v];
s=max(s,siz[v]);
}
s=max(s,sum-siz[u]);
if(s<Max){
Max=s;
root=u;
}
}
inline void Get(int u,int p){
root=0;
sum=Max=p;
getroot(u,0);
getroot(root,0);
}
inline void getdis(int u,int fa,ll d){
dis[++cnt]={u,d};
for(auto t:E[u]){
int v=t.first,w=t.second;
if(v==fa||del[v])
continue;
getdis(v,u,d+w);
}
}
inline void calc(int u){
cnt=0;
getdis(u,0,0);
sort(dis+1,dis+cnt+1);
top=0;
for(int i=1;i<=cnt;i++){
while(top&&dis[i].second<=dis[T[top]].second){
add(dis[i].first,dis[T[top]].first);
top--;
}
T[++top]=i;
}
top=0;
for(int i=cnt;i>=1;i--){
while(top&&dis[i].second<=dis[T[top]].second){
add(dis[i].first,dis[T[top]].first);
top--;
}
T[++top]=i;
}
}
inline void solve(int u){
calc(u);
del[u]=1;
for(auto t:E[u]){
int v=t.first;
if(del[v])
continue;
Get(v,siz[v]);
solve(root);
}
}
void work(){
Lowbit::init();
LCA::init();
Get(1,n);
solve(root);
}
};
bool End;
int main(){
// open("A.in","A.out");
n=read();
for(int u,v,w,i=1;i<n;i++){
u=read(),v=read(),w=read();
add(u,v,w);
}
q=read();
for(int l,r,i=1;i<=q;i++){
l=read(),r=read();
Q[l].push_back({i,r});
}
Tree::work();
for(int i=n;i>=1;i--){
for(auto v:G[i])
Lowbit::add(v,LCA::dis(i,v));
for(auto t:Q[i])
ans[t.first]=Lowbit::query(t.second);
}
for(int i=1;i<=q;i++){
write(ans[i]==INF?-1:ans[i]);
putchar('\n');
}
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}
P9058 [Ynoi2004] rpmtdq 与 P9678 [ICPC2022 Jinan R] Tree Distance的更多相关文章
- SQLite R*Tree 模块测试
目录 SQLite R*Tree 模块测试 1.SQLite R*Tree 模块特性简介 2.SQLite R*Tree 模块简单测试代码 SQLite R*Tree 模块测试 相关参考: MySQL ...
- UPC 2224 Boring Counting (离线线段树,统计区间[l,r]之间大小在[A,B]中的数的个数)
题目链接:http://acm.upc.edu.cn/problem.php?id=2224 题意:给出n个数pi,和m个查询,每个查询给出l,r,a,b,让你求在区间l~r之间的pi的个数(A< ...
- hdu 4630 查询[L,R]区间内任意两个数的最大公约数
No Pain No Game Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- HDU 6356 (线段树-l,r 之间小于val 的变val+单点求值)
题目描述: 给你一个长度为n的最开始为0的数以及m个更新操作以及数据生成器参数X,Y,Z.每次操作,将由数据生成器生成出li,ri,vi.让你从区间[li,ri]中,将所有小于vi的数变为vi.最后让 ...
- R树-javascript代码实现过程分析(插入操作)
R Tree 第一步,创建R树类. 构建一个RTree生成器.用以创建tree对象. 例子:var tree = new RTree(12) var RTree = function(width){ ...
- 整体二分QAQ
POJ 2104 K-th Number 时空隧道 题意: 给出一个序列,每次查询区间第k小 分析: 整体二分入门题? 代码: #include<algorithm> #include&l ...
- AC日记——楼房 codevs 2995
2995 楼房 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 地平线(x轴)上有n个矩(lou ...
- AC日记——滑动窗口 洛谷 P1886
题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值. 例如: The array i ...
- AC日记——忠诚 洛谷 P1816
题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了 ...
- 北京培训记day5
高级数据结构 一.左偏树&斜堆 orz黄源河论文 合并,插入,删除根节点 打标记 struct Node { int fa,l,r,w,dep } tree[Mx]; int Merge(in ...
随机推荐
- Vue学习:17.组件通信案例-记事本
通过上一节的学习,我们了解并掌握了组件通信的定义及一般使用.那么接下来,我们将之前练习过的案例使用组件化思想来实现一下吧. 实例:记事本(组件化) 实现功能 运用组件化思想,实现Vue学习:3.V标签 ...
- 一种复习flex布局的方法
方法论 flex布局有多个属性,时常会忘记.我们复习的话,单纯看一些博客文章,不能直观的理解,也比较枯燥. 因此如果有一种用写代码闯关的方式来复习(学习)flex布局,那也许会更有意思. FLEXBO ...
- nordic—RTC+PPI定时驱动某外设做非单次触发(本次测试为驱动GPIO口做电平翻转)
简介:在nordic的开发中使用到RTC时,对于比较通道0/1/2/3的中断来说,如果不进行相关配置(如SDK中例子,使用的RTC比较通道就只能触发一次,不能多次触发),会导致比较中断只进入一次,如果 ...
- mysql数据库慢SQL优化
mysql数据库慢SQL优化优化来源: 阿里云 云数据库RDS 慢sql 或者CAT监控系统中的Transaction SQL or URL根据平均时间反馈来排查,决定是否增加索引,或者调整业务逻辑代 ...
- redis zset 延迟合并任务处理
redis zset 延迟合并任务处理 @Autowired public RedisTemplate redisTemplate; ##1.发送端:在接口中收集任务ID,累计时间段之后,合并处理. ...
- git客户端安装和使用
需要安装三个软件 1.git客户端 点击下载 下载完成后一只next就行了. 2.git右键属性的扩展程序 点击下载 下载完成后一只next就行了 3.git中文包 点击下载 下载完成后一只next就 ...
- CSS 属性计算
CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢? 有的同学可能会讲,CSS属性我倒是知道,例如: p{ color : red; } 上面的 CSS 代码中,p 是元素选择器,color ...
- python重拾第十一天-REDIS缓存数据库
缓存数据库介绍 NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系数据库在应付we ...
- 安卓Camera-HAL显示值与比例
安卓Camera-HAL显示值与比例 参考:https://blog.csdn.net/wang714818/article/details/78049649?utm_source=blogxgwz4 ...
- 【基础整理】Mapping representation 机器人所用地图种类及相关介绍
参考与前言 本文主要介绍 建图 Mapping 方面的一些 基础知识介绍与相关下游任务使用 涉及知识较为基础,SLAM大佬们可以提前退出了 主要针对应用为移动机器人与物流无人驾驶车:提前申明:大部分文 ...