题解

一眼就是线段树维护点分树的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. 服务器安全策略之《启用Windows 防火墙情况下配置允许SQL Server远程连接》

    为了服务器的安全,通常情况下我们需要启用Windows 防火墙,然而在启用了Windows 防火墙后会发现会引发一系列的问题,比如无法通过本地远程服务器桌面.无法在本地远程连接SQL Server等, ...

  2. 科学计算三维可视化---Mayavi可视化实例

    一:Dragon绘制实例(三维扫描的绘制) 三维扫描主要用于对物体空间外形结构以及色彩进行扫描,用以获得物体表面的空间坐标, 他的主要意义在于能够将实物的立体信息转换为计算机能够直接处理的数据信号,为 ...

  3. bzoj千题计划115:bzoj1024: [SCOI2009]生日快乐

    http://www.lydsy.com/JudgeOnline/problem.php?id=1024 枚举横着切还是竖着切,一边儿分多少块 #include<cstdio> #incl ...

  4. IntelliJ IDEA连接cvs超时Error refreshing view: Timeout while trying to connect to host

    在使用IntelliJ IDEA连接cvs的时候,有时会报超时错误: Error refreshing view: Timeout while trying to connect to host: 1 ...

  5. java 根据二叉树前序 ,中序求后续

    在一棵二叉树总,前序遍历结果为:ABDGCEFH,中序遍历结果为:DGBAECHF,求后序遍历结果. 我们知道: 前序遍历方式为:根节点->左子树->右子树 中序遍历方式为:左子树-> ...

  6. 逆序对 inversion

      评测传送门 [问题描述] 有一个1 − n的排列,你会依次进行m次操作,第i次操作表示为(x i , y i ),交换以这两个 值为下标的元素,每次操作有一半的概率成功,你需要求出最后序列的逆序对 ...

  7. jquery 根据后台传过来的值动态设置下拉框、单选框选中

    更多内容推荐微信公众号,欢迎关注: jquery  根据后台传过来的值动态设置下拉框.单选框选中 $(function(){ var sex=$("#sex").val(); va ...

  8. 说说C语言运算符的“优先级”与“结合性”

    论坛和博客上常常看到关于C语言中运算符的迷惑,甚至是错误的解读.这样的迷惑或解读大都发生在表达式中存在着较为复杂的副作用时.但从本质上看,仍然是概念理解上的偏差.本文试图通过对三个典型表达式的分析,集 ...

  9. torch.Tensor.view (Python method, in torch.Tensor)

    返回具有相同数据但大小不同的新张量.返回的张量共享相同的数据,必须具有相同数量的元素,但可能有不同的大小. Example >>> x = torch.randn(4, 4) > ...

  10. 【C++】数组-二分法查找

    1.原理 对于给定值的查找,如果大于该数组的中间元素,下一步在元素值大的区域继续与其中间元素比较:否则下一步在元素值小的区域内继续查找,直到找到目标元素.如果到最后还没有找到,则输出"数组中 ...