• 题目链接:

    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. Oracle 11g win32位 window7下安装教程

    1.首先是去http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html官网下载oracle11 ...

  2. ajax调用c#后端,发现参数没数值

    之前是int的数据,名字是id 后面被改成字符串的数据,名字是encrptedId 因为名字不匹配,导致找不到数值.只需要把js里调用传递的参数名字改一下,或者C#后端,继续保持原来的名字

  3. vue2.0+vue-dplayer实现hls播放

    vue2.0+vue-dplayer实现hls播放 开始 安装依赖 npm install vue-dplayer -S 1,编写组件HelloWorld.vue <template> & ...

  4. smarty建的mvc环境

    ================================搭建MVC结构================================基于MVC,解耦合 (高内聚,低耦合),优点:易维护.易扩 ...

  5. 【leetcode_easy】543. Diameter of Binary Tree

    problem 543. Diameter of Binary Tree 题意: 转换一种角度来看,是不是其实就是根结点1的左右两个子树的深度之和呢.那么我们只要对每一个结点求出其左右子树深度之和,这 ...

  6. FTP与SFTP的区别

    简介 wikipedia - FTP --- FTP File Transfer wikipedia - SFTP --- SSH File Transfer 对比 缩写 名称 协议与端口 安全策略 ...

  7. VSCode 怎么运行代码

    VSCode 怎么运行代码,集成终端和资源管理器也有互动.比如我们打开了一个脚本文件,希望直接执行这个脚本文件,就可以打开命令面板,运行 “在活动终端中运行活动文件”(Run Active File ...

  8. Linux基础(特基本的那种)知识

    (自己的随手笔记,记得有点乱请轻喷) which:查看某个命令的完整路径df -h:查看系统磁盘情况history:查看历史输入的命令 网卡配置路径:vim /etc/sysconfig/networ ...

  9. 2019icpc-徐州网络赛

    B. hxc写的 AC code: #pragma GCC optimize(2) #include <cstdio> #include <queue> #include &l ...

  10. centos7ping www.baidu.com没有ping通

    在linux中ping www.baidu.com 无法ping通,可能原因是DNS没配置好 1.修改vi /etc/resolv.conf 增加如下内容: nameserver 114.114.11 ...