写在前面:由于太弱,希望各位大佬能来博客灌水,提出建议。
洛谷题目地址:https://www.luogu.org/problemnew/show/SP913
此题洛谷博客地址:https://www.luogu.org/blog/f9107l/solution-sp913
明显的LCA问题,用了树剖。思路是所有会一点树剖的人都能看懂的。
先不谈树剖的构造,先看问题如何解决,然后再用原来树剖思路构造并延伸。
好了,问题都整理了一下,但貌似仍然不好写,所以要了解树剖的关键性质。
所以接下来如何构造树剖就知道了,只是在原来基础上多了前缀和和编号数组(因为代码能力有限,又用了一个数组记录重儿子与此点相连边的序号)
代码如下,仅供参考因为太烂了
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
#define re register int
inline int read(){
int x=0,ff=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*ff;
}
struct tree{
int to,next,kk;
}a[20005];
int h[20005],tot;
int n,fa[20005],ss[20005],o[20005],d[20005],op[20005],s[20005];
int cnt,id[20005],as[20005],sl[20005],oo[20005];
void add(int x,int y,int z){
a[++tot].next=h[x];a[tot].to=y;a[tot].kk=z;h[x]=tot;
}
void dfs1(int i,int la,int dp){
fa[i]=la;d[i]=dp;s[i]=1;
int x=-1;
for(int j=h[i];j;j=a[j].next){
if(a[j].to==la)continue;
dfs1(a[j].to,i,dp+1);
s[i]+=s[a[j].to];
if(s[a[j].to]>x){
x=s[a[j].to];o[i]=a[j].to;oo[i]=j;
}
}
}
void dfs2(int i,int tp){
op[i]=tp;id[i]=++cnt;as[cnt]=i;
if(!o[i])return;
dfs2(o[i],tp);
ss[id[i]]=ss[id[o[i]]]+a[oo[i]].kk;
for(int j=h[i];j;j=a[j].next){
if(a[j].to==fa[i])continue;
sl[cnt+1]=a[j].kk;
if(a[j].to==o[i])continue;
dfs2(a[j].to,a[j].to);
}
}
//上面的构造都基本是模板,略微的更改都已提到了
void lca(int x,int y){
int ans=0;
while(op[x]!=op[y]){
if(d[op[x]]<d[op[y]])swap(x,y);
ans+=sl[id[op[x]]]+ss[id[op[x]]]-ss[id[x]];x=fa[op[x]];//性质2+经典前缀和
}
if(d[x]>d[y])swap(x,y);
ans+=ss[id[x]]-ss[id[y]];
printf("%d\n",ans);
}
void find(int x,int y,int z){
int yy=y,xx=x;
while(op[xx]!=op[yy]){
if(d[op[xx]]<d[op[yy]])swap(xx,yy);
xx=fa[op[xx]];
}
if(d[xx]>d[yy])swap(xx,yy);
if(d[x]-d[xx]+1>=z){
while(1){
if(z<=d[x]-d[op[x]]+1)break;
z-=d[x]-d[op[x]]+1;x=fa[op[x]];
//上面两个语句都根据性质1
}
printf("%d\n",as[id[x]-z+1]);//性质2
}
else {
z-=d[x]-d[xx];//性质1
int u=d[y]-d[xx]+1;//性质1
while(1){
if(op[y]==op[xx])break;
if(z>u-(d[y]-d[op[y]]+1))break;
u-=(d[y]-d[op[y]]+1);
//上面两句也是性质1
y=fa[op[y]];
}
if(op[y]==op[xx]){
printf("%d\n",as[z-(u-(d[y]-d[xx]+1))+id[xx]-1]);//性质2
return;
}
printf("%d\n",as[z-(u-(d[y]-d[op[y]]+1))+id[op[y]]-1]);//性质2
}
}
//find过程中有一些小细节,自己画图就知道了。
signed main(){
int t=read();
while(t--){
n=read();int x,y,z;
memset(h,0,sizeof(h));tot=0;
memset(ss,0,sizeof(ss));
memset(s,0,sizeof(s));
memset(sl,0,sizeof(sl));
cnt=0;
memset(fa,0,sizeof(fa));
memset(o,0,sizeof(o));
for(int i=1;i<n;i++){
x=read();y=read();z=read();
add(x,y,z);add(y,x,z);
}
cnt=0;dfs1(1,0,1);dfs2(1,1);
char ch[105];
scanf("%s",ch);
while(ch[1]!='O'){
if(ch[1]=='I'){
x=read();y=read();
lca(x,y);
}
else {
x=read();y=read();z=read();
find(x,y,z);
}
scanf("%s",ch);
}}
return 0;
}
此题最后的坑点是多组数据,别忘了清空记录重儿子的数组
洛谷-题解 SP913【QTREE2 - Query on a tree II】
原文:https://www.cnblogs.com/ffrxy01bt/p/11232441.html