Qtree1-7

Qtree1 裸的树链剖分,当然也可以用LCT写,就不说什么了...

Qtree2 倍增lca,当然也可以用LCT写,就不说什么了...

Qtree3 裸的树链剖分,当然也可以用LCT写,就不说什么了...

接下来的Qtree4-7才是重点

推荐顺序(7->6->4->5),因为写了7就可以改一改贴到6,写了4就可以改一改贴到5了,瞬间少写两题

Qtree4 我已经想到了一个很好的LCT写法,只可惜这里空白太小,写不下了

Qtree5更简单一些,推荐先写5,或者写这题的不带边权版bzoj1095

—————————————————————————分割线——————————————————————————————

这题的做法很多,有动态树分治,树分治和其他的算法

我的写法是受到Qtree7的启发,听说Qtree7LCT的做法可以扩展到这题,于是就写了一发

这题用到的是Qtree7的两个思想之一,就是用一个数据结构维护虚边连接的子树的信息。

这题是用set维护。

首先把边权附到儿子结点上,记为len(len会引发很多细节问题,一定要小心)

实边上的信息就维护一个lmax,rmax,maxs,sum,类似线段树维护最长连续0/1串的长度

虚边就类似点分治时的想法,记录只到x点的链,和x子树中已经构成路径的两个白点(只管虚子树)

但是定义有一些区别

lmax这个子splay表示的一段重链最浅的白点出发的最长链长,rmax从该实链最深的白点出发的最长链长,maxs即答案,包括实边虚边的最远两白点距离 ,sum表示该子splay段实链总长

为了叙述方便,其他的一些定义:ls左儿子,rs有儿子

先讨论虚子树对maxs的影响,实边要复杂一些

最长和次长到x的链可以合并成一条路径

子树里已经形成的最大路径也可以更新

如果x是白点,那么最长链就可以更新了

虚子树信息的维护主要是在access中的虚实切换

对于要从实变虚的原右儿子,maxs[rs]扔进路径的set里,lmax[rs]扔进链的set里,因为要到x点,所以是lmax

同样,从虚变实的新右儿子,把maxs[y]从set里删去,把lmax[y]从set里删去

虚部就完成了

对于实链我们可以发现一个性质,LCT中splay的任意一个子树,对应的都是原树中实链连续的一段

我们在线段树的update合并左右区间时不可能向左向右暴力找到最远的1,更新答案

同样,我们也不能在splay里找到原树中紧邻x上面的点,把它splay上来更新答案

但一个合法的路径要穿过x点,就要过紧邻x上面的点,和紧邻x下面的点

所以我们要记lmax,rmax

然后就是写到吐血激动人心的update了

先更新lmax和rmax

lmax要过整棵子splay的最浅点

那么有lmax[x]=max(lmax[ls]//不过x

,max(虚链中最长+整个左儿子代表的一段,lmax[rs]+整个左儿子代表的一段))

rmax同理,注意x对应的这条边,会有一些细节问题

于是maxs就又有了两种新的更新方式

maxs[x]=max(maxs[x],rmax[rs]+max(虚链,lmax[rs]))

maxs[x]=max(maxs[x],lmax[rs]+max(虚链,rmax[ls]))

然后更新方式就完了

然后就没有然后了

O(nlog^2)卡常警报

不过当set的size比较大时,说明虚边很多,树的深度不深,LCT的操作次数要少一些

如果树的深度深,那么set的size又比较小,所以两个log并不严格

#include<bits/stdc++>
const int maxn=200010,maxm=200010,inf=1e9;
using namespace std;
int n,Q,pre[maxm],now[maxn],son[maxm],val[maxm],tot,ans=-inf,col[maxn];char op[5];//0白1黑
void add(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}
inline int fir(multiset<int> &s){return s.size()?*s.rbegin():-inf;}//最大,因为c++是左闭右开,所以s.end()不是最后一个,要--s.end()才是
inline int sec(multiset<int> &s){return s.size()>1?*(++s.rbegin()):-inf;}struct Tlct{#define ls ch[x][0]#define rs ch[x][1]int ch[maxn][2],fa[maxn],lmax[maxn],rmax[maxn],maxs[maxn],sum[maxn],len[maxn],w[maxn];//lmax这个子splay表示的一段重链最浅的点出发的最长链长,rmax从该实链最深的点出发的最长链长,maxs即答案,最远两白点距离 //len当前点和父亲之间的边长度,sum该段实链总长 w表示颜色 白色为0表示有一个白点,距离可以是0,黑色为-inf,表示无白点multiset<int> chain[maxn],path[maxn];//chain 链 path 路径 用来存虚边信息inline int which(int x){return ch[fa[x]][1]==x;}inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}void init(){for (int i=0;i<=n;i++) lmax[i]=rmax[i]=maxs[i]=-inf;}void update(int x){assert(x);sum[x]=sum[ls]+sum[rs]+len[x];int cha=max(w[x],fir(chain[x]));int L=max(cha,rmax[ls]+len[x]);//从左子树(实链中更浅的点)或虚边的白点来的最远链长int R=max(cha,lmax[rs]);//同理lmax[x]=max(lmax[ls],sum[ls]+len[x]+R);//要想清楚加不加len[x]rmax[x]=max(rmax[rs],sum[rs]+L);maxs[x]=max(rmax[ls]+len[x]+R,lmax[rs]+L);maxs[x]=max(maxs[x],max(maxs[ls],maxs[rs]));maxs[x]=max(maxs[x],fir(path[x]));maxs[x]=max(maxs[x],fir(chain[x])+sec(chain[x]));if (w[x]==0) maxs[x]=max(max(maxs[x],fir(chain[x])),0);//如果这是白点,就可以用子树中的链更新}void rotate(int x){assert(x);int y=fa[x],z=fa[y],nx=which(x),ny=which(y);fa[ch[x][!nx]]=y,ch[y][nx]=ch[x][!nx];fa[x]=z;if (!isroot(y)) ch[z][ny]=x;fa[y]=x,ch[x][!nx]=y;update(y);}void splay(int x){for (;!isroot(x);){int y=fa[x];if (isroot(y)) rotate(x);else if (which(x)==which(y)) rotate(y),rotate(x);else rotate(x),rotate(x);}update(x);}void access(int x){for (int y=0;x;y=x,x=fa[x]){splay(x);if (rs) path[x].insert(maxs[rs]),chain[x].insert(lmax[rs]);if (y) path[x].erase(path[x].find(maxs[y])),chain[x].erase(chain[x].find(lmax[y]));rs=y,update(x);}}void modify(int x){access(x),splay(x);col[x]^=1,w[x]=!col[x]?0:-inf;update(x),ans=maxs[x];//for (int i=1;i<=3;i++) print(i);}void print(int x){printf("x%d fa%d ls%d rs%d lmax%d rmax%d maxs%d sum%d len%d\n",x,fa[x],ls,rs,lmax[x],rmax[x],maxs[x],sum[x],len[x]);}
}T;void dfs(int x){for (int y=now[x];y;y=pre[y]) if (son[y]!=T.fa[x]){T.fa[son[y]]=x,T.len[son[y]]=val[y],dfs(son[y]);T.chain[x].insert(T.lmax[son[y]]),T.path[x].insert(T.maxs[son[y]]);}T.update(x);
}int main(){scanf("%d",&n),T.init();for (int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);dfs(1),ans=T.maxs[1],scanf("%d",&Q);for (int i=1,x;i<=Q;i++){scanf("%s",op+1);if (op[1]=='A'){if (ans<0) puts("They have disappeared.");else printf("%d\n",ans);}else scanf("%d",&x),T.modify(x);}return 0;
}/*
6
1 2 1
1 3 1
3 4 2
3 5 2
3 6 2
7
A
C 4
C 5
C 6
A
*/

Qtree5

qtree4的弱化版,注意初始都是是黑点,求的是最近

Qtree6

很显然,树链剖分是可捉此题的

开两棵树,一棵白,一棵黑,记siz[x][0/1]表示子树中还有多少与之连通的同色点

再维护dfs序最小的连通的同色点(线段树里就记最左边的1的位置)

修改颜色时,把x到该点的路径上,重链就区间减,轻链就暴力减

——————————————————————————LCT写法————————————————————————————

也是记两棵树,一棵黑一颗白,对于虚边的siz直接用上面的方法,新开一个siz数组维护一下就好了

但是还有一个问题没有解决,如果改色时暴力link-cut,显然菊花图时修改的边数可以达到O(n)级别

于是就有了一个巧妙的写法,当一个点从白变黑时,只在白树里cut掉它和父亲的边,只在黑树里link上它和父亲的边

也就是保证黑白树中的每条边的儿子一定是黑/白色的,父亲则可能是其他颜色

询问时找到最浅的点,白点就在白树里询问,黑点就在黑树里询问,但要注意根要特判

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010,maxm=200010;
using namespace std;
int n,Q,pre[maxm],now[maxn],son[maxm],tot,f[maxn],col[maxn];char ch;
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void read(int &x){for (ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());int t;if (ch=='-') t=-1,ch=getchar();else t=1;for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x=x*t;
}struct Tlct{#define ls ch[x][0]#define rs ch[x][1]int fa[maxn],ch[maxn][2],siz[maxn],s[maxn];inline int which(int x){return ch[fa[x]][1]==x;}inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}void update(int x){siz[x]=siz[ls]+siz[rs]+s[x]+1;}void rotate(int x){int y=fa[x],z=fa[y],nx=which(x),ny=which(y);fa[ch[x][!nx]]=y,ch[y][nx]=ch[x][!nx];fa[x]=z;if (!isroot(y)) ch[z][ny]=x;fa[y]=x,ch[x][!nx]=y;update(y);}void splay(int x){for (;!isroot(x);){int y=fa[x];if (isroot(y)) rotate(x);else if (which(x)==which(y)) rotate(y),rotate(x);else rotate(x),rotate(x);}update(x);}void access(int x){for (int y=0;x;y=x,x=fa[x]){splay(x);if (rs) s[x]+=siz[rs];if (y) s[x]-=siz[y];rs=y,update(x);}}void link(int x){access(f[x]),splay(f[x]),splay(x);ch[f[x]][1]=x,fa[x]=f[x],update(f[x]);}void cut(int x){access(x),splay(x),fa[ls]=0,ls=0,update(x);}int getroot(int x){access(x),splay(x);while (ls) x=ls;return x;}int query(int x){int c=col[x];x=getroot(x),splay(x);return col[x]==c?siz[x]:siz[rs];}
}T[2];void dfs(int x){for (int y=now[x];y;y=pre[y]) if (son[y]!=f[x]){f[son[y]]=T[col[son[y]]].fa[son[y]]=x;dfs(son[y]),T[col[son[y]]].s[x]+=T[col[son[y]]].siz[son[y]];}T[0].update(x),T[1].update(x);
}int main(){scanf("%d",&n);for (int i=1,x,y;i<n;i++) read(x),read(y),add(x,y),add(y,x);dfs(1),read(Q);for (int i=1,op,x;i<=Q;i++){read(op),read(x);if (!op) printf("%d\n",T[col[x]].query(x));else{if (f[x]) T[col[x]].cut(x),T[col[x]^1].link(x);col[x]^=1;}}return 0;
}

Qtree7

和Qtree6类似,维护虚边信息用set,因为维护最大值不能像维护siz一样直接加和减,所以要开一个set,细节稍微多一点

#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010,maxm=200010;
using namespace std;
int n,Q,pre[maxm],now[maxn],son[maxm],tot,col[maxn],f[maxn];char ch;
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b;}
void read(int &x){for (ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());int t;if (ch=='-') t=-1,ch=getchar();else t=1;for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x=x*t;
}struct Tlct{#define ls ch[x][0]#define rs ch[x][1]int ch[maxn][2],fa[maxn],maxs[maxn],val[maxn];multiset<int> s[maxn];inline bool isroot(int x){return (ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x);}inline bool which(int x){return ch[fa[x]][1]==x;}inline void update(int x){maxs[x]=val[x];if (s[x].size()) maxs[x]=max(maxs[x],*s[x].rbegin());if (ls) maxs[x]=max(maxs[x],maxs[ls]);if (rs) maxs[x]=max(maxs[x],maxs[rs]);}void rotate(int x){int y=fa[x],z=fa[y],nx=which(x),ny=which(y);fa[ch[x][!nx]]=y,ch[y][nx]=ch[x][!nx];fa[x]=z;if (!isroot(y)) ch[z][ny]=x;fa[y]=x,ch[x][!nx]=y;update(y);}void splay(int x){for (;!isroot(x);){int y=fa[x];if (isroot(y)) rotate(x);else if (which(x)==which(y)) rotate(y),rotate(x);else rotate(x),rotate(x);}update(x);}void access(int x){for (int y=0;x;y=x,x=fa[x]){splay(x);if (rs) s[x].insert(maxs[rs]);rs=y;if (y) s[x].erase(s[x].find(maxs[y]));update(x);}}int getroot(int x){access(x),splay(x);while (ls) x=ls;return x;}void link(int x){access(f[x]),splay(f[x]),splay(x);ch[f[x]][1]=x,fa[x]=f[x],update(f[x]);}void cut(int x){access(x),splay(x),fa[ls]=0,ls=0,update(x);}void modify(int x,int v){access(x),splay(x),val[x]=v,update(x);}int query(int x){int c=col[x];x=getroot(x),splay(x);return c==col[x]?maxs[x]:maxs[rs];}
}T[2];void dfs(int x){for (int y=now[x];y;y=pre[y]) if (son[y]!=f[x]){f[son[y]]=T[col[son[y]]].fa[son[y]]=x;dfs(son[y]),T[col[son[y]]].s[x].insert(T[col[son[y]]].maxs[son[y]]);}T[0].update(x),T[1].update(x);
}int main(){scanf("%d",&n);for (int i=1,x,y;i<n;i++) read(x),read(y),add(x,y),add(y,x);for (int i=1;i<=n;i++) read(col[i]);for (int i=1;i<=n;i++) read(T[0].val[i]),T[1].val[i]=T[0].val[i];dfs(1),read(Q);for (int i=1,op,x,y;i<=Q;i++){read(op),read(x);if (op==0) printf("%d\n",T[col[x]].query(x));if (op==1){if (f[x]) T[col[x]].cut(x),T[col[x]^1].link(x);col[x]^=1;}if (op==2) read(y),T[0].modify(x,y),T[1].modify(x,y);}return 0;
}

【Qtree】Query on a tree系列LCT解法相关推荐

  1. Query on a tree(LCT版)

    文章目录 题目 分析 代码 题目 Query on a tree 分析 动态树(LCT)初探 代码 #include<cstdio> #include<vector> #inc ...

  2. SPOJ - QTREE Query on a tree(树链剖分+线段树)

    题目链接:点击查看 题目大意:给出一棵由n个点组成的树,再给出数个操作,每次操作分为下列几种类型: QUERY x y:询问点x-点y这条路径上的所有边权的最大值 CHANGE x y:将第x条边的权 ...

  3. [SPOJ375]QTREE - Query on a tree【树链剖分】

    题目描述 给你一棵树,两种操作. 修改边权,查找边权的最大值. 分析 我们都知道,树链剖分能够维护点权. 而且每一条边只有一个,且唯一对应一个儿子节点,那么就把信息放到这个儿子节点上. 注意,lca的 ...

  4. SPOJ 375. Query on a tree (树链剖分)

    题目链接: http://www.spoj.com/problems/QTREE/ 375. Query on a tree Problem code: QTREE You are given a t ...

  5. XXI Open Cup. Grand Prix of Korea I. Query On A Tree 17 树剖 + 二分 + 树带权重心

    传送门 文章目录 题意: 思路: 题意: 给你一棵树,每棵树初始权值都为000,现在给你两个操作: (1)(1)(1)将uuu的子树权值全部加111. (2)(2)(2)将(u,v)(u,v)(u,v ...

  6. [BZOJ1095][ZJOI2007]捉迷藏 Query on a tree IV(树链剖分)

    首先,我们求出树的重链,然后对于每一条链,建一颗线段树 树大概长这样: (其中用红边连起来的是一条条重链) 在线段树上,我们维护: Opt(u):经过 u节点代表的链的其中一段 的两个白点间的最长路径 ...

  7. LCA SP913 QTREE2 - Query on a tree II

    SP913 QTREE2 - Query on a tree II 给定一棵n个点的树,边具有边权.要求作以下操作: DIST a b 询问点a至点b路径上的边权之和 KTH a b k 询问点a至点 ...

  8. hdu 4836 The Query on the Tree(线段树or树状数组)

    The Query on the Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot ...

  9. spoj 375 Query on a tree (树链剖分)

    题目链接: http://www.spoj.com/problems/QTREE/ 题意: 给一颗树,每条边有一个权值.有两种操作: 1.修改某条边的值: 2.询问a.b两点路径上边权的最大值. 分析 ...

最新文章

  1. python yield与递归
  2. Android技能树 — LayoutInflater Factory小结
  3. 基于节拍谱的语音音乐分类模型
  4. 信息学奥赛一本通(1102:与指定数字相同的数的个数)
  5. 全国计算机一级msoffice考试内容,2015年全国计算机一级MSOffice考试大纲
  6. python selenium中文文档-selenium-python中文版文档
  7. spring security oauth2.0 实现
  8. AdneneBoumessouer / MVTec-Anomaly-Detection学习笔记
  9. python读取dat文件写入表格_Pandas:外部文件数据导入/ 读取 (如 :csv、txt、tsv、dat、excel文件)、文件存储(to_csv、to_excel)...
  10. oracle 错误代码问题处理经验汇总
  11. 《Java 就业培训教程》
  12. java+ selenium截图
  13. 《打造Facebook》 读书报告
  14. c语言中judge的用法,judge的用法总结大全
  15. 51nod1521 一维战舰
  16. [转]file_get_contents(php://input)
  17. Linpack安装、配置与运行
  18. excel(2015)表格如何在滑动时固定标题栏
  19. mac下密码加密zip,暴力破解zip,生成密钥字典
  20. VisionPro控件的使用 C# 开发篇

热门文章

  1. 《青春有你2》选手信息爬取
  2. 洛谷4550 收集邮票(期望DP)
  3. IntelliJ IDEA设置自动导入包
  4. 认识css长度单位 px % em rem vh vw
  5. 帆软BI中界面上如果要替换原有的组件,两种替换方式
  6. linux下hwclock不能同步时间到硬件时钟
  7. 物流行业SaaS多租用商城系统:提升企业物流管理效率,实现高效协同
  8. input框输入身份证时实现动态脱敏
  9. MathType如何正确卸载?
  10. 手机中好用的软件有哪些?让我来告诉你吧