[xyz模拟题]动态维护树的直径

专出神题的xyz。

支持删加边、修改点权、维护树的直径。

LCT  需要额外记录子树信息。用一个堆维护。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
using namespace std;
#define rep(i,x,y) for(i=x;i<=y;i++)
#define _rep(i,x,y) for(i=x;i>=y;i--)
#define REP(i,x,y) for(int i=(x);i<=(y);i++)
#define ALL(x,S) for(x=S.begin();x!=S.end();x++)
#define mp make_pair
#define fi first
#define se second
#define pb push_back
template<class T> inline void read(T&x){bool fu=0;char c;for(c=getchar();c<=32;c=getchar());if(c==‘-‘)fu=1,c=getchar();for(x=0;c>32;c=getchar())x=x*10+c-‘0‘;if(fu)x=-x;};
template<class T> inline void read(T&x,T&y){read(x);read(y);}
template<class T> inline void read(T&x,T&y,T&z){read(x);read(y);read(z);}
inline char getc(){char c;for(c=getchar();c<=32;c=getchar());return c;}

typedef long long ll;
typedef pair<int,int> pii;
int max(int a,int b,int c){return max(max(a,b),c);}

const int N=100010;
const int inf=int(1e9);
int n,m,i,j,k,x,y,u,v,w0,w1;

multiset<int> son[N],AN;
multiset<int>::iterator pos;

priority_queue<pii> Q;

int ch[N][2],fa[N],key[N];bool rev[N];
int lw[N],rw[N],ans[N],len[N];

void Hins(int i,int t){son[i].insert(lw[t]);}
void Hdel(int i,int t){son[i].erase(son[i].find(lw[t]));}
int mson(int i){return son[i].empty()?0:*son[i].rbegin();}
int tson(int i){if(son[i].size()<=1)return mson(i);pos=--son[i].end();return *pos+*(--pos);}

bool tp(int i){return !(ch[fa[i]][0]==i||ch[fa[i]][1]==i);}
void REV(int i){if(i){rev[i]=!rev[i];swap(ch[i][0],ch[i][1]);swap(lw[i],rw[i]);}}
void D(int i){if(rev[i])rev[i]=0,REV(ch[i][0]),REV(ch[i][1]);}
void U(int i)
{
#define L ch[i][0]
#define R ch[i][1]
len[i]=len[L]+key[i]+len[R];
ans[i]=max(rw[L]+lw[R],max(rw[L],lw[R])+mson(i),tson(i))+key[i];
lw[i]=max(lw[L],len[L]+key[i]+mson(i),len[L]+key[i]+lw[R]);
rw[i]=max(rw[R],len[R]+key[i]+mson(i),len[R]+key[i]+rw[L]);
Q.push(mp(ans[i],i));
}

void rot(int i,int t){int x,y;x=ch[i][!t];D(x);y=ch[x][t];ch[i][!t]=y;ch[x][t]=i;if(y)fa[y]=i;if(fa[i])if(ch[fa[i]][0]==i)ch[fa[i]][0]=x;else if(ch[fa[i]][1]==i)ch[fa[i]][1]=x;fa[x]=fa[i];fa[i]=x;U(i);}
void spl(int x){int y,z;D(x);while(!tp(x)){y=fa[x];z=fa[y];if(z)D(z);if(y)D(y);if(tp(y))rot(y,ch[y][0]==x);else if(ch[z][0]==y)if(ch[y][0]==x)rot(z,1),rot(y,1);else rot(y,0),rot(z,1);else if(ch[y][0]==x)rot(y,1),rot(z,0);else rot(z,0),rot(y,0);U(x);}}
void acc(int x){for(int i=0,t;x;i=x,x=fa[x]){spl(x);t=ch[x][1];if(t)Hins(x,t);ch[x][1]=i;if(i){fa[i]=x;Hdel(x,i);}U(x);}}
void maker(int x){acc(x);spl(x);REV(x);}
void change(int x,int y){acc(x);spl(x);key[x]=y;U(x);}
void cut(int x,int y){maker(x);acc(y);spl(y);ch[y][0]=0;fa[x]=0;U(y);}
void link(int x,int y){maker(x);acc(y);spl(y);fa[x]=y;Hins(y,x);U(y);}
vector<int> E[N];void dfs(int i){vector<int>::iterator it;ALL(it,E[i])if((*it)!=fa[i])fa[*it]=i,dfs(*it),Hins(i,*it);U(i);}
void solve(){pii u;while(1){u=Q.top();if(ans[u.se]==u.fi)break;Q.pop();}printf("%d\n",u.fi);}

int main()
{
freopen("longest.in","r",stdin);freopen("longest.out","w",stdout);
read(n,m);
rep(i,1,n-1)read(x,y),E[x].pb(y),E[y].pb(x);
rep(i,1,n)read(key[i]);
dfs(1);
solve();
for(;m;m--)
{
char c=getc();
if(c==‘M‘)read(x,y),change(x,y);
else if(c==‘C‘)read(x,y),read(u,v),cut(x,y),link(u,v);
solve();
}
return 0;
}

[xyz模拟题]动态维护树的直径

时间: 2024-11-05 09:16:32

[xyz模拟题]动态维护树的直径的相关文章

欧拉序动态维护树直径

https://zhuanlan.zhihu.com/p/84236967 https://www.cnblogs.com/TinyWong/p/11260601.html 一个月过去了,我还是没有学动态点分治... 欧拉序保存了每个节点进入和返回的情况,$n$ 个结点的树,欧拉序列长度为 $2n - 1$. 两个结点的LCA就是它们在欧拉序中出现的位置的区间中,深度最小的那个结点. 对于边权为正的树,上面定义中的深度也可以换成到根的距离,用dis[u]表示结点 $u$ 到根的距离. 那么两个节

「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径

题目传送门 http://192.168.21.187/problem/1236 http://47.100.137.146/problem/1236 题解 题目中要求的显然是那个状态下的直径嘛. 所以这道题有一个非常简单的做法--线段树分治. 直接把每一条边按照 \(l, r\) 的区间放到线段树上进行分治,遍历的时候用并查集维护直径就可以了. 时间复杂度为 \(O(n\log^2n)\). 很早以前就写了这个算法,代码附在了最后,不多讲了. 但是这道题还有一个方法--动态 DP. 线段树分治

倍增/线段树维护树的直径 hdu5993/2016icpc青岛L

题意: 给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径 点10W,询问10W,询问相互独立 Solution: 考虑线段树/倍增维护数的直径 考虑dfs序的一个区间 [l, r] 是联通的 而我们知道了有 l <= k < r, 且知道 [l, k] 和 [k + 1, r] 两个区间的直径端点及长度 假设两个区间的直径端点分别为 (l1, r1) 和 (l2, r2) 那么 [l, r] 这个区间的直径长度为 dis(l1, r1) dis(l1, l1)  dis(l1, r2)

SDUT OJ 3045 迷之图论 (树的直径)

题目地址:SDUT OJ 3045 这题比赛的时候想的差不多..但是总是觉得不对..写了一次就没再写,然后删了..当时没想到的是第二次求出来的就是最长链..当时想到的两次bfs找最大值(这一种方法其实结果也对..TAT..),还有找到点后在回溯减去重点等等..但总觉得好像都不太对...赛后才知道这题原来是树的直径.....牡丹江区域现场赛的时候遇到过,不过赛后也没看... 找树的直径的方法其实就是先任取一点进行bfs,找到最远的一点,这时最远的一点肯定是最长链端点之一,然后再从这一最远点开始bf

CodeForces 379F 树的直径 New Year Tree

题意:每次操作新加两个叶子节点,每次操作完以后询问树的直径. 维护树的直径的两个端点U,V,每次计算一下新加进来的叶子节点到U,V两点的距离,如果有更长的就更新. 因为根据树的直径的求法,若出现新的直径,一定是到U或者到V距离最远. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 const int maxn = 1000000

BZOJ 1901 Zju 2112 Dynamic Rankings 动态维护第k小 树套树

题目大意:动态维护第k小. 思路:线段树套treap,裸题,就是不怎么好写. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 #define INF 1e9 #define LEFT (pos << 1) #define RIGHT (pos << 1|1) #define SIZ

[模拟赛10.12] 老大 (二分/树的直径/树形dp)

[模拟赛10.12] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n ? 1 条边的无向连通图),由于新建的办公室太大以至于要将奖杯要分放在两个不同的地方以便同学们丢硬币进去开光,OB 想请你帮帮他看看奖杯放在哪两个办公室使得在任意一个在劳模办公室做题的小朋友能最快地找到奖杯来开光. 一句话题意:给出一个 n 个点的树,在两个合适且不同的点放上奖杯,使得每个点到最近的奖杯距离最大值最小. 输入

【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】

目录: 1.A[树相关]    2.B[找规律]    3.C[贪心][拓扑排序] A. 描述(A 输入文件 : A.input 输出文件 : A.output)一个城市的构成是一颗n 个节点的树(2 ≤ n ≤ 200), 现在需要在树中找出两条不相交的路径(即两条路径不能有重边也不能有重点),使得路径的长度的乘积最大.输入描述第一行一个数n 表示这个城市一共有 n 个节点.接下来 n-1 行,每行两个数ai 和bi (1 ≤ ai,bi ≤ n ),分别表示从ai 到bi,有一条边,每条边的

【bzoj4184】shallot 线段树+高斯消元动态维护线性基

题目描述 小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏. 每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且 让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大. 这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗? 你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0. 输入 第一行一个正整数n表示总时间:第二行n个整数a1,a2...an,