Loj #6073.「2017 山东一轮集训 Day5」距离
Loj #6073.「2017 山东一轮集训 Day5」距离
Description
给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\),设$ path(u,v) \(表示\) u$ 到 $v \(的路径,\)dist(u,v) \(表示\) u$ 到\(v\) 的距离,希望你求出
Input
第一行一个整数 \(type =0/1\)表示这个测试点的数据类型。
第二行两个整数 \(n,q\)。
接下来$ n−1$ 行,每行三个整数 \(ui,vi,ci,\)代表树上有一条连接$ ui,vi$ 的权值为$ci $的边。
接下来一行 \(n\) 个正整数表示给定的排列 p。
接下来 \(q\) 行,每行三个整数 \(u′,v′,k′\),记lastAns 为上一次询问的答案,假如这是第一次则\(lastAns=0\),那么这个询问对应的\(u,v,k\) 满足:
思路还是比较妙啊。
题目的难点在于求一个点与一个点集的\(lca\)深度之和。我们可以将点集中的每个点到根的路径上的标记都\(+1\)。询问点\(k\)到这个点集的\(lca\)深度和的时候我们就可以询问该点到根路径上的所有边权与标记的乘积之和。
由于是询问\((a,b)\)路径上的信息,我们就用主席树维护,询问的时候做差分。对于一个节点\(a\),它的信息由\(fa_a\)继承下来,再加上\(p_a\)的信息就好了。
考试时不会,写的虚树,常数大到自闭。
[HNOI2015]开店 的主席树做法也是基于这个原理的。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
int n,m,type;
int p[N];
ll pre[N];
ll ans;
struct road {
int to,next;
ll d;
}s[N<<1];
int h[N],cnt;
void add(int i,int j,ll d) {s[++cnt]=(road) {j,h[i],d};h[i]=cnt;}
ll sum_dis[N];
int size[N],son[N];
int fa[N],top[N],dep[N];
ll dis[N];
void dfs(int v) {
size[v]=1;
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
if(to==fa[v]) continue ;
dep[to]=dep[v]+1;
dis[to]=dis[v]+s[i].d;
fa[to]=v;
dfs(to);
size[v]+=size[to];
if(size[son[v]]<size[to]) son[v]=to;
}
}
int dfn[N],id;
void dfs2(int v,int tp) {
sum_dis[v]=sum_dis[fa[v]]+dis[p[v]];
dfn[v]=++id;
pre[dfn[v]]=dis[v]-dis[fa[v]];
top[v]=tp;
if(son[v]) dfs2(son[v],tp);
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
if(to==fa[v]||to==son[v]) continue ;
dfs2(to,to);
}
}
int lca(int a,int b) {
while(top[a]!=top[b]) {
if(dep[top[a]]<dep[top[b]]) swap(a,b);
a=fa[top[a]];
}
return dep[a]<dep[b]?a:b;
}
int tot;
int rt[N];
int lx,rx;
struct tree {
int ls,rs;
ll tag,sum;
}tr[N*150];
void Modify(int &v,int old,int lx,int rx,int l,int r,ll f) {
if(lx>r||rx<l) return ;
v=++tot;
tr[v]=tr[old];
if(l<=lx&&rx<=r) {
tr[v].tag+=f;
return ;
}
int L=max(lx,l),R=min(rx,r);
tr[v].sum+=f*(pre[R]-pre[L-1]);
int mid=lx+rx>>1;
Modify(tr[v].ls,tr[old].ls,lx,mid,l,r,f);
Modify(tr[v].rs,tr[old].rs,mid+1,rx,l,r,f);
}
void Modify(int v) {
int a=p[v];
while(top[a]!=top[1]) {
Modify(rt[v],rt[v],lx,rx,dfn[top[a]],dfn[a],1);
a=fa[top[a]];
}
Modify(rt[v],rt[v],lx,rx,dfn[1],dfn[a],1);
}
ll query(int v,int lx,int rx,int l,int r) {
if(!v||lx>r||rx<l) return 0;
if(l<=lx&&rx<=r) return tr[v].sum+tr[v].tag*(pre[rx]-pre[lx-1]);
ll ans=tr[v].tag*(pre[min(r,rx)]-pre[max(l,lx)-1]);
int mid=lx+rx>>1;
ans+=query(tr[v].ls,lx,mid,l,r)+query(tr[v].rs,mid+1,rx,l,r);
return ans;
}
ll query(int rt,int a) {
ll ans=0;
while(top[a]!=top[1]) {
ans+=query(rt,lx,rx,dfn[top[a]],dfn[a]);
a=fa[top[a]];
}
ans+=query(rt,lx,rx,dfn[1],dfn[a]);
return ans;
}
void dfs3(int v) {
rt[v]=rt[fa[v]];
Modify(v);
for(int i=h[v];i;i=s[i].next) {
int to=s[i].to;
if(to==fa[v]) continue ;
dfs3(to);
}
}
int main() {
type=Get();
n=Get(),m=Get();
int a,b,d;
for(int i=1;i<n;i++) {
a=Get(),b=Get(),d=Get();
add(a,b,d),add(b,a,d);
}
for(int i=1;i<=n;i++) p[i]=Get();
lx=1,rx=n;
dfs(1);
dfs2(1,1);
for(int i=1;i<=n;i++) pre[i]+=pre[i-1];
dfs3(1);
while(m--) {
ll x=Get()^(ans*type),y=Get()^(ans*type),k=Get()^(ans*type);
int f=lca(x,y);
ans=sum_dis[x]+sum_dis[y]-sum_dis[f]-sum_dis[fa[f]];
ans+=(dep[x]+dep[y]-2*dep[f]+1)*dis[k];
ans-=2*(query(rt[x],k)+query(rt[y],k)-query(rt[f],k)-query(rt[fa[f]],k));
cout<<ans<<"\n";
}
return 0;
}
Loj #6073.「2017 山东一轮集训 Day5」距离的更多相关文章
- loj#6073. 「2017 山东一轮集训 Day5」距离(树链剖分 主席树)
题意 题目链接 Sol 首先对询问差分一下,我们就只需要统计\(u, v, lca(u, v), fa[lca(u, v)]\)到根的路径的贡献. 再把每个点与\(k\)的lca的距离差分一下,则只需 ...
- loj#6073. 「2017 山东一轮集训 Day5」距离(费用流)
题意 题目链接 Sol 我们可以把图行列拆开,同时对于行/列拆成很多个联通块,然后考虑每个点所在的行联通块/列联通块的贡献. 可以这样建边 从S向每个行联通块连联通块大小条边,每条边的容量为1,费用为 ...
- 「2017 山东一轮集训 Day5」距离
/* 写完开店再写这个题目顿时神清气爽, 腰也不疼了, 眼也不花了 首先考虑将询问拆开, 就是查询一些到根的链和点k的关系 根据我们开店的结论, 一个点集到一个定点的距离和可以分三部分算 那么就很简单 ...
- Loj #6069. 「2017 山东一轮集训 Day4」塔
Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...
- Loj 6068. 「2017 山东一轮集训 Day4」棋盘
Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...
- 「2017 山东一轮集训 Day5」苹果树
「2017 山东一轮集训 Day5」苹果树 \(n\leq 40\) 折半搜索+矩阵树定理. 没有想到折半搜索. 首先我们先枚举\(k\)个好点,我们让它们一定没有用的.要满足这个条件就要使它只能和坏 ...
- LOJ #6074. 「2017 山东一轮集训 Day6」子序列
#6074. 「2017 山东一轮集训 Day6」子序列 链接 分析: 首先设f[i][j]为到第i个点,结尾字符是j的方案数,这个j一定是从i往前走,第一个出现的j,因为这个j可以代替掉前面所有j. ...
- loj #6077. 「2017 山东一轮集训 Day7」逆序对
#6077. 「2017 山东一轮集训 Day7」逆序对 题目描述 给定 n,k n, kn,k,请求出长度为 n nn 的逆序对数恰好为 k kk 的排列的个数.答案对 109+7 10 ^ 9 ...
- LOJ #6119. 「2017 山东二轮集训 Day7」国王
Description 在某个神奇的大陆上,有一个国家,这片大陆的所有城市间的道路网可以看做是一棵树,每个城市要么是工业城市,要么是农业城市,这个国家的人认为一条路径是 exciting 的,当且仅当 ...
随机推荐
- ABP框架 sql语句(转载)
ABP.Core实现SQL语句仓储,支持EF.Core兼容的数据库 来源:https://blog.csdn.net/qq_28699537/article/details/80522680?tds ...
- axios 用法简介(转载)
axios 来源:https://www.jianshu.com/p/df464b26ae58 一.安装 1. 利用npm安装npm install axios --save2. 利用bower安 ...
- [android] 帧布局
/*******************2016年5月3日 更新**************************************/ 知乎:如何理解andriod中的View和framela ...
- [angularjs] angularjs系列笔记(七)HTML DOM
AngularJs为HTML DOM元素的属性提供了绑定数据的指令 ng-disabled指令 ng-disabled指令直接绑定数据到HTML元素的disabled属性 ng-show指令 ng-s ...
- nginx常用命令参数
命令行参数: 常用命令: -c filename:设置配置文件. -t :不运行,而仅仅测试配置文件.nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件. -s :传递一个信 ...
- 【Mybatis】MyBatis调用带有返回结果、output参数的存储过程上与ibatis的区别
用过mybatis的应该都知道它是ibatis被Google收购后重新命名的一个工程,因此也做了大量升级.本文就来介绍下两者在调用存储过程上的一点区别,ibatis有一个专门的标签<proced ...
- Ios12怎么投屏到电脑 苹果手机投屏电脑的方法
苹果手机是一款外观精致时尚的手机,吸引这许多用户争先恐后的购买,然后苹果手机的系统不同于我们传统使用的安卓系统,它采用的是苹果自主研发的IOS系统,有许多操作与安卓手机不同,那么苹果手机怎么用呢?下面 ...
- leaflet计算多边形面积
上一篇介绍了使用leaflet绘制圆形,那如何计算圆形的面积呢? 1.使用数学公式计算,绘制好圆形后,获取中心点以及半径即可 2.使用第三方工具计算,如turf.js. 这里turf的area方法入参 ...
- Nginx http相关常用配置总结
Nginx http相关常用配置总结 by:授客 QQ:1033553122 测试环境 nginx-1.10.0 client_max_body_size Syntax: client_ma ...
- 小米手机Toast显示带应用名称问题解决方法
近期为了适配刘海屏,向公司申购了一步小米8的手机,然后测试人员那边测出来一堆适配的问题,其中有一个每一个Toast会显示app的名称+显示的内容,然后网上查找了一下解决方法记录一下,顺便封装了Toas ...