• 题目链接:

    https://www.luogu.org/problemnew/show/P4092

  • 瞎扯--\(O(Q \log^3 N)\)解法

    这道先yy出了一个\(O(Q \log^3 N)\),的做法,先树链剖分。

    对于加标记操作,找到那个点所在的链,将其\(top\)标记一下,然后该点到根节点区间和+1.

    对于查询操作,先看这个点所在链有没有标记,如果没有,就一直向上跳直到找到一条标记了的链,然后在那条链上根据到根节点区间和进行倍增/二分

    然后出去吃饭的时候忽然想到了\(O(Q \log^2 N)\)的解法,于是刚刚这个解法刚打完还没有查错,放在这做一个参考

    代码:

include

include

include

include

include

include

include

define ll long long

define ri register int

using namespace std;

const int maxn=100005;

const int inf=0x7fffffff;

template inline void read(T &x){

x=0;int ne=0;char c;

while(!isdigit(c=getchar()))ne=c'-';

x=c-48;

while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;

x=ne?-x:x;

return ;

}

int n,q;

struct Edge{

int ne,to;

}edge[maxn<<1];

int h[maxn],num_edge=0;

inline void add_edge(int f,int t){

edge[++num_edge].ne=h[f];

edge[num_edge].to=t;

h[f]=num_edge;

return ;

}

int dep[maxn],fa[maxn],size[maxn],son[maxn],top[maxn],dfn[maxn],rnk[maxn],cnt=0;

void dfs_1(int now){

int v;size[now]=1;

for(ri i=h[now];i;i=edge[i].ne){

v=edge[i].to;

if(vfa[now])continue;

fa[v]=now,dep[v]=dep[now]+1;

dfs_1(v);

size[now]+=size[v];

if(!son[now]||size[son[now]]<size[v])son[now]=v;

}

return ;

}

void dfs_2(int now,int t){

int v;top[now]=t;

dfn[now]=++cnt,rnk[cnt]=now;

if(!son[now])return ;

dfs_2(son[now],t);

for(ri i=h[now];i;i=edge[i].ne){

v=edge[i].to;

if(vfa[now]||vson[now])continue;

dfs_2(v,v);

}

return ;

}

int sum[maxn<<2],tag[maxn<<2],L,R,dta,ok[maxn];

void build(int now,int l,int r){

if(lr){

sum[now]=ok[rnk[l]];

return ;

}

int mid=(l+r)>>1;

build(now<<1,l,mid);

build(now<<1|1,mid+1,r);

return ;

}

void pushdown(int now,int ln,int rn){

if(tag[now]){

sum[now<<1]+=tag[now]ln;

sum[now<<1|1]+=tag[now]
rn;

tag[now<<1]+=tag[now];

tag[now<<1|1]+=tag[now];

tag[now]=0;

}

return ;

}

void update(int now,int l,int r){

if(L<=l&&r<=R){

sum[now]+=dta*(r-l+1);

tag[now]+=dta;

return ;

}

int mid=(l+r)>>1;

pushdown(now,mid-l+1,r-mid);

if(L<=mid)update(now<<1,l,mid);

if(mid<R)update(now<<1|1,mid+1,r);

sum[now]=sum[now<<1]+sum[now<<1|1];

return ;

}

int query(int now,int l,int r){

if(L<=l&&r<=R){

return sum[now];

}

int mid=(l+r)>>1,ans=0;

pushdown(now,mid-l+1,r-mid);

if(L<=mid)ans+=query(now<<1,l,mid);

if(mid<R)ans+=query(now<<1|1,mid+1,r);

sum[now]=sum[now<<1]+sum[now<<1|1];

return ans;

}

void update_path(int x,int y){

dta=1;ok[top[x]]=1;//该条链上有一个标记的点

while(top[x]!=top[y]){

if(dep[top[x]]<dep[top[y]])swap(x,y);

L=dfn[top[x]],R=dfn[x];

update(1,1,n);

}

if(dfn[x]<dfn[y])swap(x,y);

L=dfn[x],R=dfn[y];

update(1,1,n);

return ;

}

inline int solve(int x,int y){

int tmp,val,p=0,k=1,len,ans=0;

bool flag=0;

while(top[x]!=top[y]){

if(dep[top[x]]<dep[top[y]])swap(x,y);

len=dfn[x]-dfn[top[x]];

if(ok[top[x]]){

L=dfn[top[x]],R=dfn[x],

tmp=query(1,1,n);

p=0,k=1,flag=0;

while(k!=0){

L=dfn[x+p+k],R=dfn[x];

if(query(1,1,n)>tmp)flag=1,k=k>>1;

else p=p+k,k=k<<1;

while(p+k>len)k=k>>1;

}

if(flag)return ans+dfn[x+p]-dfn[x];

}

ans+=len;

x=fa[top[x]];

}

if(dfn[x]>dfn[y])swap(x,y);

L=dfn[x],R=dfn[y],len=dfn[y]-dfn[x];

tmp=query(1,1,n);

p=0,k=1;

//cout<<y<<endl;

if(xy)return ans;

while(k!=0){

L=dfn[x+p+k],R=dfn[x];

if(query(1,1,n)>tmp)k=k>>1;

else p=p+k,k=k<<1;

//if(y3)cout<<k<<' '<<p<<endl;

while(p+k>len)k=k>>1;

}

return ans+dfn[x+p]-dfn[x];

}

int main(){

char opt[5];

int x,y,z;

read(n),read(q);

for(ri i=1;i<n;i++){

read(x),read(y);

add_edge(x,y);

add_edge(y,x);

}

dep[1]=1,fa[1]=0;

dfs_1(1);

dfs_2(1,1);

ok[dfn[1]]=1;

build(1,1,n);

while(q--){

scanf("%s",opt);

if(opt[0]'C'){

read(x);

//cout<<x<<"-----"<<endl;

update_path(1,x);

}

else{

read(x);

//cout<<x<<"***"<<endl;

printf("%d\n",solve(x,1));

}

}

return 0;

}


- 分析---$O(Q \log^2 N)$解法 首先我想到了一个错误的解法,就是因为链是线段树上一个连续的区间,每个$[dfn[x],dfn[top[x]]]$线段树区间有个$mx$值,表示,$x$到$top[x]$路径中距离它最近标记的祖先,加标记时比较原有标记深度与新标记深度然后更新。查询的时候查询$x$到$top[x]$的区间最大之就可以了,如果没有,就一直往上跳直至找到 然而这个解法有个错误我SB地没有发现,就是你更新区间最大值时,$x$上的祖先节点也会被更新到(因为深度更小),再次感谢wjyyy和creed_两位大佬指出我的错误 正解应该是更新子树,将子树的最大值更新,查询照样,相比于我错误的代码只需改一句话 代码:

include

include

include

include

include

include

include

define ll long long

define ri register int

using namespace std;

const int maxn=100005;

const int inf=0x7fffffff;

template inline void read(T &x){

x=0;int ne=0;char c;

while(!isdigit(c=getchar()))ne=c'-';

x=c-48;

while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;

x=ne?-x:x;

return ;

}

int n,q;

struct Edge{

int ne,to;

}edge[maxn<<1];

int h[maxn],num_edge=0;

inline void add_edge(int f,int t){

edge[++num_edge].ne=h[f];

edge[num_edge].to=t;

h[f]=num_edge;

return ;

}

int dep[maxn],fa[maxn],size[maxn],son[maxn],top[maxn],dfn[maxn],rnk[maxn],cnt=0;

void dfs_1(int now){

int v;size[now]=0;

for(ri i=h[now];i;i=edge[i].ne){

v=edge[i].to;

if(vfa[now])continue;

fa[v]=now,dep[v]=dep[now]+1;

dfs_1(v);

size[now]+=size[v];

if(!son[now]||size[son[now]]<size[v])son[now]=v;

}

return ;

}

void dfs_2(int now,int t){

int v;top[now]=t;

dfn[now]=++cnt,rnk[cnt]=now;

if(!son[now])return ;

dfs_2(son[now],t);

for(ri i=h[now];i;i=edge[i].ne){

v=edge[i].to;

if(vfa[now]||vson[now])continue;

dfs_2(v,v);

}

return ;

}

int mx[maxn<<2],L,R,dta;

void build(int now,int l,int r){

if(lr){

if(rnk[l]1)mx[now]=1;

else mx[now]=0;

return ;

}

int mid=(l+r)>>1;

build(now<<1,l,mid);

build(now<<1|1,mid+1,r);

if(dep[mx[now<<1]]>dep[mx[now<<1|1]]){

mx[now]=mx[now<<1];

}

else mx[now]=mx[now<<1|1];

return ;

}

void update(int now,int l,int r){

if(L<=l&&r<=R){

if(dep[mx[now]]<dep[dta]){

mx[now]=dta;

}

return ;

}

int mid=(l+r)>>1;

if(L<=mid)update(now<<1,l,mid);

if(mid<R)update(now<<1|1,mid+1,r);

if(dep[mx[now<<1]]>dep[mx[now<<1|1]]){

mx[now]=mx[now<<1];

}

else mx[now]=mx[now<<1|1];

return ;

}

int query(int now,int l,int r){

if(L<=l&&r<=R){

return mx[now];

}

int mid=(l+r)>>1,ans=0,tmp;

if(L<=mid){

int tmp=query(now<<1,l,mid);

if(dep[ans]<dep[tmp])ans=tmp;

}

if(mid<R){

int tmp=query(now<<1|1,mid+1,r);

if(dep[ans]<dep[tmp])ans=tmp;

}

return ans;

}

void update_path(int x){

dta=x;

//L=R=dfn[x];

L=dfn[x],R=dfn[x]+size[x];

update(1,1,n);

return ;

}

int query_path(int x){

int ans=0;

while(top[x]!=1){

L=dfn[top[x]],R=dfn[x];

ans=query(1,1,n);

if(ans!=0)return ans;

x=fa[top[x]];

}

L=dfn[1],R=dfn[x];

ans=query(1,1,n);

return ans;

}

int main(){

char opt[5];

int x,y,z;

read(n),read(q);

for(ri i=1;i<n;i++){

read(x),read(y);

add_edge(x,y);

add_edge(y,x);

}

dep[0]=-1,dep[1]=1,fa[1]=0;

dfs_1(1);

dfs_2(1,1);

build(1,1,n);

while(q--){

//cout<<q<<endl;

scanf("%s",opt);

if(opt[0]=='C'){

read(x);

update_path(x);

}

else{

read(x);

printf("%d\n",query_path(x));

}

}

return 0;

}

```

luogu题解 P4092 【[HEOI2016/TJOI2016]树】树链剖分的更多相关文章

  1. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...

  2. 洛谷 P4092 [HEOI2016/TJOI2016]树 || bzoj4551

    https://www.lydsy.com/JudgeOnline/problem.php?id=4551 https://www.luogu.org/problemnew/show/P4092 这当 ...

  3. 线段树&数链剖分

    傻逼线段树,傻逼数剖 线段树 定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 使用线段树可以快速的查找某一个节点在若干条线段中出现 ...

  4. [洛谷P4092][HEOI2016/TJOI2016]树

    题目大意:给你一棵树,有两个操作: $C\;x:$给第$x$个节点打上标记 $Q\;x:$询问第$x$个节点的祖先中最近的打过标记的点(自己也是自己的祖先) 题解:树剖,可以维护区间或,然后若一段区间 ...

  5. P4092 [HEOI2016/TJOI2016]树

    题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作: 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记 ...

  6. 题解 【HEOI2016】tree树

    题面 解析 其实这题可以考虑离线做法,用并查集解决. 因为仔细想,添加标记并不方便, 但如果用并查集记录下祖先, 再一一删除,就会方便很多. 先把每次操作记录下来, 同时记录下每个点被标记的次数(因为 ...

  7. [题解] LuoguP4091 [HEOI2016/TJOI2016]求和

    传送门 首先我们来看一下怎么求\(S(m,n)\). 注意到第二类斯特林数的组合意义就是将\(m\)个不同的物品放到\(n\)个没有区别的盒子里,不允许有空盒子的方案数. 那么将\(m\)个不同的物品 ...

  8. 树链剖分好(du)题(liu)选做

    1.luogu P4315 月下"毛景树" 题目链接 前言: 这大概是本蒟蒻A掉的题里面码量最大的一道题了.我自认为码风比较紧凑,但还是写了175行. 从下午2点多调到晚上8点.中 ...

  9. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

随机推荐

  1. ubuntu下安装g++

    主要来自ubuntu中文社区http://www.ubuntu.org.cn/support/documentation/doc/VMware 首选,确认你已经安装了build-essential程序 ...

  2. CDH构建大数据平台-配置集群的Kerberos认证安全

     CDH构建大数据平台-配置集群的Kerberos认证安全 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 当平台用户使用量少的时候我们可能不会在一集群安全功能的缺失,因为用户少,团 ...

  3. 007-log-log4j2、slf4j+log4j2

    一.概述 而log4j2的性能无论在同步日志模式还是异步日志模式下都是最佳的. 根本原因在于log4j2使用了LMAX, 一个无锁的线程间通信库代替了, logback和log4j之前的队列. 并发性 ...

  4. oracle 中SQL 语句开发语法 SELECT INTO含义

    oracle 中SQL 语句开发语法 SELECT INTO含义 在ORACLE中SELECT INTO是如何使用的,什么意思?和SQL SERVER的不一样?   和sqlserver的不一样sql ...

  5. windows7安装docker异常:looks like something went wrong in step ‘looking for vboxmanage.exe’

    一.背景 最近准备抽点时间研究下docker,选择在家中的windows系统上安装. 我的系统是windows7,首先安装Docker Toolbox,Docker Toolbox是一个工具集,主要包 ...

  6. Linux发行版本简介

    Linux发行版 1.      Linux本身 1.1.        1991年,当时一名来自赫尔辛基的计算机科学学生LinusTorvalds创建了一个操作系统内核 1.1.1.     一年后 ...

  7. 图解 HTTP 笔记(一)——了解 Web 及网络基础

    本章内容:Web 建立在何种技术之上,HTTP 协议如何诞生并发展? 一.Web 基于 HTTP 通信 Web 使用一种名为 HTTP (HyperText Transfer Protocol,超文本 ...

  8. git 命令常用笔记

    1. 全局操作 git --version //git 机器上是否存在 git init --bare project.git //服务端:初始化一个新的仓库 chown -R zhangsan:zh ...

  9. Go语言中使用切片(slice)实现一个Vector容器

    Go语言中的切片(slice)和一些内置函数能实现其他语言容器类Array.Vector的功能,但是Go内置语言包container里只提供了list.heap.ring三种容器,缺少vector容器 ...

  10. Intellij IDEA 激活到2100年教程

    1.先看效果   2.下载 JetbrainsCrack-4.2-release-enc.jar,提取码:9os6.并将下载后的文件复制到$ {InstallDir} / bin,$ {Install ...