题解

一眼就是线段树维护点分树的dfs序嘛

代码debug一年(手动再见)

码力直线下降,坐等滚粗= =

很明显的我们需要一个点分树,然后求出以每个重心为根的树的dfs序,线段树维护一下每个点的价值-每个点到根的距离

对于修改点直接单点修改,对于边相当于修改了一个子树到根的距离,就是dfs序上一段区间的加减

然后查询点分树里除掉这个点所在子树的区间,查询两边区间的最大值即可

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 100005
#define mp make_pair
#define pb push_back
#define fi first
#define se second
//#define ivorysi
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {putchar('-');x = -x;}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int Line[MAXN],idx;
int64 D[MAXN];
pair<int64,int> Max(pair<int64,int> a,pair<int64,int> b) {
if(a.fi != b.fi) return a.fi < b.fi ? b : a;
else return a.se < b.se ? a : b;
}
struct Segment_Tree {
struct Tr_node {
int L,R,lc,rc;
int64 lazy;
pair<int64,int> S;
}tr[MAXN * 40];
int Ncnt;
#define lc(u) tr[u].lc
#define rc(u) tr[u].rc
void update(int u) {
tr[u].S = Max(tr[lc(u)].S,tr[rc(u)].S);
}
void addlazy(int u,int64 v) {
tr[u].S.fi += v;
tr[u].lazy += v;
}
void pushdown(int u) {
if(tr[u].lazy) {
addlazy(lc(u),tr[u].lazy);
addlazy(rc(u),tr[u].lazy);
tr[u].lazy = 0;
}
}
void build(int &u,int L,int R) {
u = ++Ncnt;
tr[u].L = L;tr[u].R = R;
if(L == R) {
tr[u].S = mp(D[Line[L]],Line[L]);
return;
}
int mid = (L + R) >> 1;
build(tr[u].lc,L,mid);
build(tr[u].rc,mid + 1,R);
update(u);
}
void Add(int u,int l,int r,int64 v) {
if(tr[u].L == l && tr[u].R == r) {addlazy(u,v);return;}
int mid = (tr[u].L + tr[u].R) >> 1;
pushdown(u);
if(r <= mid) Add(lc(u),l,r,v);
else if(l > mid) Add(rc(u),l,r,v);
else {Add(lc(u),l,mid,v),Add(rc(u),mid + 1,r,v);}
update(u);
}
pair<int64,int> Query(int u,int l,int r) {
if(r < l) return mp(-1e18,-1);
if(tr[u].L == l && tr[u].R == r) return tr[u].S;
pushdown(u);
int mid = (tr[u].L + tr[u].R) >> 1;
if(r <= mid) return Query(lc(u),l,r);
else if(l > mid) return Query(rc(u),l,r);
else return Max(Query(lc(u),l,mid),Query(rc(u),mid + 1,r));
}
}SegTr;
struct node {
int to,next;int64 val;
}E[MAXN * 2];
int head[MAXN],sumE;
void add(int u,int v,int64 c) {
E[++sumE].to = v;
E[sumE].next = head[u];
E[sumE].val = c;
head[u] = sumE;
}
struct PointDivideTree {
vector<int> Fa,dfn,aux,siz;
vector<int64> Fa_dis;
int rt;
}PD[MAXN];
int N,Q;
int64 z[MAXN];
bool vis[MAXN];
int siz[MAXN],son[MAXN],fa[MAXN];
int que[MAXN],ql,qr;
int calcG(int st) {
ql = 1,qr = 0;
que[++qr] = st;fa[st] = 0;
while(ql <= qr) {
int u = que[ql++];
siz[u] = 1;son[u] = 0;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(!vis[v] && fa[u] != v) {
fa[v] = u;
que[++qr] = v;
}
}
}
int res = que[qr];
for(int i = qr ; i >= 1 ; --i) {
int u = que[i];
son[u] = max(son[u],qr - siz[u]);
if(son[u] < son[res]) res = u;
siz[fa[u]] += siz[u];
if(siz[u] > son[fa[u]]) son[fa[u]] = siz[u];
}
return res;
}
int Calc(int u,int fa,int64 fa_dis,int G) {
int s = 1;
++idx;
Line[idx] = u;
D[u] = D[fa] + fa_dis;
PD[u].aux.pb(G);
PD[u].Fa.pb(fa);
PD[u].dfn.pb(idx);
PD[u].Fa_dis.pb(fa_dis);
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(!vis[v] && v != fa) {
s += Calc(v,u,E[i].val,G);
}
}
PD[u].siz.pb(s);
return s;
}
void pre(int u) {
int G = calcG(u);
vis[G] = 1;
idx = 0;D[G] = 0;
Calc(G,0,0,G);
for(int i = 1 ; i <= idx ; ++i) D[Line[i]] = z[Line[i]] - D[Line[i]];
SegTr.build(PD[G].rt,1,idx);
for(int i = head[G] ; i ; i = E[i].next) {
int v = E[i].to;
if(!vis[v]) pre(v);
}
}
void Init() {
read(N);read(Q);
for(int i = 1 ; i <= N ; ++i) read(z[i]);
int u,v;int64 c;
for(int i = 1 ; i < N ; ++i) {
read(u);read(v);read(c);
add(u,v,c);add(v,u,c);
}
pre(1);
}
void Solve() {
int op,u,v;
int64 w;
int st = 1;
for(int q = 1 ; q <= Q ; ++q) {
read(op);
if(op == 1) {
read(u);read(w);
int s = PD[u].aux.size();
for(int i = 0 ; i < s ; ++i) {
int G = PD[u].aux[i];
SegTr.Add(PD[G].rt,PD[u].dfn[i],PD[u].dfn[i],w - z[u]);
}
z[u] = w;
}
else {
read(u);read(v);read(w);
int s = min(PD[u].aux.size(),PD[v].aux.size());
for(int i = 0 ; i < s ; ++i) {
int rt = PD[PD[u].aux[i]].rt;
if(PD[u].Fa[i] == v) {
SegTr.Add(rt,PD[u].dfn[i],PD[u].dfn[i] + PD[u].siz[i] - 1,PD[u].Fa_dis[i] - w);
PD[u].Fa_dis[i] = w;
}
else if(PD[v].Fa[i] == u) {
SegTr.Add(rt,PD[v].dfn[i],PD[v].dfn[i] + PD[v].siz[i] - 1,PD[v].Fa_dis[i] - w);
PD[v].Fa_dis[i] = w;
}
else break;
}
}
pair<int64,int> p = mp(-1e18,-1);
int s = PD[st].aux.size();
for(int i = 0 ; i < s; ++i) {
int G = PD[st].aux[i],rt = PD[G].rt;
int64 t = -SegTr.Query(rt,PD[st].dfn[i],PD[st].dfn[i]).fi + z[st];
pair<int64,int> k;
if(i != s - 1) k = Max(SegTr.Query(rt,1,PD[st].dfn[i] - 1),SegTr.Query(rt,PD[st].dfn[i] + PD[st].siz[i],PD[G].siz[i]));
else k = SegTr.Query(rt,2,PD[G].siz[i]);
if(k.se == -1) continue;
p = Max(p,mp(k.fi - t,k.se));
}
st = p.se;
out(st);space;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
}

【LOJ】#2497. 「PA 2017」Banany的更多相关文章

  1. 【LOJ】#2290. 「THUWC 2017」随机二分图

    题解 看了一眼觉得是求出图对图统计完美匹配的个数(可能之前做过这样模拟题弃疗了,一直心怀恐惧... 然后说是统计一下每种匹配出现的概率,也就是,当前左边点匹配状态为S,右边点匹配状态为T,每种匹配出现 ...

  2. 【LOJ】#2289. 「THUWC 2017」在美妙的数学王国中畅游

    题解 我们发现,题目告诉我们这个东西就是一个lct 首先,如果只有3,问题就非常简单了,我们算出所有a的总和,所有b的总和就好了 要是1和2也是多项式就好了--其实可以!也就是下面泰勒展开的用处,我们 ...

  3. 【LOJ】#3088. 「GXOI / GZOI2019」旧词

    LOJ#3088. 「GXOI / GZOI2019」旧词 不懂啊5e4感觉有点小 就是离线询问,在每个x上挂上y的询问 然后树剖,每个节点维护轻儿子中已经被加入的点的个数个数乘上\(dep[u]^{ ...

  4. 【LOJ】#3087. 「GXOI / GZOI2019」旅行者

    LOJ#3087. 「GXOI / GZOI2019」旅行者 正着求一遍dij,反着求一遍,然后枚举每条边,从u到v,如果到u最近的点和v能到的最近的点不同,那么可以更新答案 没了 #include ...

  5. 【LOJ】#3086. 「GXOI / GZOI2019」逼死强迫症

    LOJ#3086. 「GXOI / GZOI2019」逼死强迫症 这个就是设状态为\(S,j\)表示轮廓线为\(S\),然后用的1×1个数为j 列出矩阵转移 这样会算重两个边相邻的,只要算出斐波那契数 ...

  6. 【LOJ】#3085. 「GXOI / GZOI2019」特技飞行

    LOJ#3085. 「GXOI / GZOI2019」特技飞行 这显然是两道题,求\(C\)是一个曼哈顿转切比雪夫后的线段树扫描线 求\(AB\),对向交换最大化和擦身而过最大化一定分别为最大值和最小 ...

  7. 【LOJ】#3083. 「GXOI / GZOI2019」与或和

    LOJ#3083. 「GXOI / GZOI2019」与或和 显然是先拆位,AND的答案是所有数字为1的子矩阵的个数 OR是所有的子矩阵个数减去所有数字为0的子矩阵的个数 子矩阵怎么求可以记录每个位置 ...

  8. 【LOJ】#2349. 「JOI 2017/2018 决赛」团子制作

    题解 有意思的一个dp,我们对G计数,发现如果不在同一条对角线上的G肯定不会互相影响,所以我们对于每一条对角线dp dp的方式是枚举这个G以什么方式放,横着还是竖着,还是不放 代码 #include ...

  9. 【LOJ】#2350. 「JOI 2017/2018 决赛」月票购买

    题解 首先求一个最短路图出来,最短路图就是这条边在最短路上就保留,否则就不保留,注意最短路图是一个有向图,一条边被保留的条件是 dis(S,u) + val(u,v) = dis(v,T)我们需要求两 ...

随机推荐

  1. poj 3216 Repairing Company

    http://poj.org/problem?id=3216 n个地点,m个任务 每个任务有工作地点,开始时间,持续时间 最少派多少人可以完成所有的任务 传递闭包之后最小路径覆盖 #include&l ...

  2. 八、Kafka总结

    一 Kafka概述 1.1 Kafka是什么 在流式计算中,Kafka一般用来缓存数据,Storm通过消费Kafka的数据进行计算. 1)Apache Kafka是一个开源消息系统,由Scala写成. ...

  3. Ubuntu 通过 Live CD 更新grub恢复引导Boot Menu

    工作需要更换主板,但是不想重装电脑. 怎么办呢? 其实并不需要重装电脑,只需要回复boot menu即可. 1. 首先用u盘制作一个ubuntu的live CD(请自行百度),然后通过u盘启动, 选择 ...

  4. 【译】第八篇 SQL Server代理使用外部程序

    本篇文章是SQL Server代理系列的第八篇,详细内容请参考原文 在这一系列的上一篇,学习了如何用SQL Server代理作业活动监视器监控作业活动和查看作业历史记录.在实时监控和管理SQL Ser ...

  5. Eureka简介

    Eureka是Spring Cloud Netfix 的一个子模块,也是核心模块之一,用于云端服务发现.一个基于RestFul的服务,用于定位服务,以实现云端中间层服务发现和中间层转移. 服务注册与发 ...

  6. [Openwrt扩展中篇]添加Aria2和webui

    上一篇说了我构建了简单的网络硬盘,这一篇说的是我构造的aria2和webui,大概是这样我觉得有了网络硬盘,那么我是不是可以远程下载呢,翻阅了网上资料发现迅雷的Xware貌似不更新了,然后我发现了ar ...

  7. elasticsearch6.5集群环境搭建的一些坑

    都说el配置很简单,确实比solr简单多了,不用手动配置一大堆,不过第一次配置也不轻松,因为马虎老是漏掉了许多地方 配置一个半小时才启动成功: 这里主要记录一下一些遇到的坑: 一 不能用root启动, ...

  8. Linux硬盘镜像获取与还原(dd、AccessData FTK Imager)

    1.硬盘镜像获取工具:dd dd是Linux/UNIX 下的一个非常有用的命令,作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换. 1.1 本地取数据 查看磁盘及分区 # fdisk - ...

  9. Explorer : 发布一个key-value存储系统,带有客户端和服务器端

    Explorer 一个key-value存储系统,带有客户端和服务器端.使用非常方便. 使用B+树作为存储引擎,客户端和服务器端使用TCP协议进行通信. 代码采用C++实现,底层将客户端和服务器通信封 ...

  10. Tomcat安装与优化

    Tomcat安装与优化 1.安装jdk环境 最新的JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downlo ...