P4092 [HEOI2016/TJOI2016]树
原题链接P4092 [HEOI2016/TJOI2016]树
分析
写这篇题解是因为好像用了个跟大家都不一样的思路去写了这道题目。
这题用的知识点依旧是:树剖+线段树
关键在于,我们维护的是什么?在这里,我们考虑维护一个区间是否存在染色的点
接下来,我们分别说说这两个操作中具体怎么操作。
标记操作
这里很好说,我们只用进行单点修改即可
void modify(int u,int x)
{
if(tr[u].l==tr[u].r)//当查询到对应结点时停止。
{
tr[u].st = 1;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x<=mid) modify(u<<1,x);
else modify(u<<1|1,x);
pushup(u);
}
询问操作
接下来,就是重头戏了。我们如何进行询问?
在这里我用了类似于深搜的方式进行询问。
对于一个点y
,我们想求其最近的被染色的祖先,需要我们不断的沿链向上回溯。
当然,这步很基础。接下来,我们具体说说如何实现搜索式的查询。
- 想找到距离y最近的被染色祖先,那我们在递归时就先从线段的右儿子找起(若右儿子有被染色的节点)则我们直接向其中递归,找到那个节点。
- 否则,若是右儿子里,我们就去左儿子里面找(如果左儿子中有的话)
- 若左右儿子都没有的话,我们直接返回-1表示该区间没有我们想要找到的点
我们来看看代码。
int query(int u,int l,int r)
{
if(tr[u].l==tr[u].r) return rid[tr[u].l];//rid表示的是,在线段树中的节点,在实际的树中的编号。
int mid = tr[u].l + tr[u].r >> 1;
int res = -1;//初始化为-1
if(r>mid&&tr[u<<1|1].st) res = query(u<<1|1,l,r);//若右儿子有染色的节点,优先递归右儿子
if(res==-1&&l<=mid&&tr[u<<1].st) res = query(u<<1,l,r);//若右儿子中找不到对应的染色节点,则判断左儿子有无被染色的点,若有则递归寻找
return res;//最后返回找到的点,或返回并未找到点
}
到了这里,题目就结束了,有一点也需要注意当我们从节点不断沿链上找时,若是找到了及时退出,这样才是我们要找的答案
来看看代码
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = N<<1;
struct Node//线段树的结构体
{
int l,r;
bool st;//区间是否存在被染色节点
}tr[N<<2];
int h[N],ne[M],e[M],idx;//链式向前星
int sz[N],dep[N],fa[N],son[N];
int id[N],top[N],rid[N],ts;//这两行是树链剖分的数组,其中不太常规的是rid,其含义表示,线段树中结点所对应的实际树中的结点的编号
int n,m;
void add(int a,int b)//加边函数
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,dep[u] = depth,fa[u] = pa;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
dfs1(j,u,depth+1);
if(sz[j]>sz[son[u]]) son[u] = j;
sz[u] += sz[j];
}
}
void dfs2(int u,int tp)//树剖的初始化
{
top[u] = tp,id[u] = ++ts,rid[ts] = u;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
void pushup(int u)
{
tr[u].st = tr[u<<1].st|tr[u<<1|1].st;
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
if(l==r) return ;
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int x)
{
if(tr[u].l==tr[u].r)//当查询到对应结点时停止。
{
tr[u].st = 1;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x<=mid) modify(u<<1,x);
else modify(u<<1|1,x);
pushup(u);
}
int query(int u,int l,int r)
{
if(tr[u].l==tr[u].r) return rid[tr[u].l];//rid表示的是,在线段树中的节点,在实际的树中的编号。
int mid = tr[u].l + tr[u].r >> 1;
int res = -1;//初始化为-1
if(r>mid&&tr[u<<1|1].st) res = query(u<<1|1,l,r);//若右儿子有染色的节点,优先递归右儿子
if(res==-1&&l<=mid&&tr[u<<1].st) res = query(u<<1,l,r);//若右儿子中找不到对应的染色节点,则判断左儿子有无被染色的点,若有则递归寻找
return res;//最后返回找到的点,或返回并未找到点
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//h数组初始化
for(int i=0;i<n-1;i++)
{
int a,b;scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
modify(1,1);//千万别忘了,1号节点初始时就被染色了
while(m--)
{
char op[2];
int x;
scanf("%s%d",op,&x);
if(*op=='C') modify(1,id[x]);
else
{
int res = -1;
while(top[x]!=1)//不断沿链上翻
{
res = query(1,id[top[x]],id[x]);
if(res!=-1) break;//若已经找到,就直接退出
x = fa[top[x]];
}
if(res==-1) res = query(1,1,id[x]);
printf("%d\n",res);
}
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步