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 的,当且仅当 ...
随机推荐
- Tomcat服务器为java项目配置顶级域名
修改端口, Tomcat服务器下conf/server.xml文件 把端口号更改为80 解释:输入域名时默认进入80端口,如果没修改则需要输入端口号才能进入. Eg:www.xxx.com: ...
- 元类实现ORM
1. ORM是什么 ORM 是 python编程语言后端web框架 Django的核心思想,"Object Relational Mapping",即对象-关系映射,简称ORM. ...
- python基础学习(九)字典
字典的定义 dictionary(字典) 是 除列表以外 Python 之中 最灵活 的数据类型(有点类似java中的Map) 字典同样可以用来 存储多个数据 通常用于存储 描述一个 物体 的相关信息 ...
- canvas-a12ellipse.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- IE CSS Hack【记录】
1.条件hack 2.属性hack 3.选择器hack CSS Hack一般都是利用各浏览器的支持CSS的能力和BUG来进行的 本文只列举了一些常用的CSS Hack,且不考虑IE6以下的版本 尽可能 ...
- Filter防止用户访问一些未被授权的资源
package com.drp.util.filter; import java.io.IOException; import javax.servlet.Filter; import javax.s ...
- iphone屏幕镜像 屏幕镜像怎么用
iPhone手机投屏到电脑设备上,需要使用到AirPlay镜像功能,但是有些苹果新用户朋友一定还不知道iphone屏幕镜像怎么用吧?你要着急下面为你提供解决方法. 使用工具:iphone.电脑 操作方 ...
- (三)版本控制管理器之CVS(下)
在上一篇文章<(二)版本控制管理器之CVS(上)>中,我为大家介绍了什么是CVS.CVS的特点.CVS的安装.CVSNT服务器的配置.TortoiseCVS客户端的配置等,本篇文章继续为大 ...
- Python 套接字socketserver网络编程
为什么使用socketserver 虽然Python内置的socket和threading模块能实现简单的多线程服务器,在非正式环境,随便用用还是可以的,但是如果要在生产环境中使用,那是万万不够的. ...
- Python之__new__方法
# -*- coding: utf-8 -*- """ Created on Sun Dec 2 11:03:03 2018 Python类构造过程 @author: z ...