http://acm.hust.edu.cn/vjudge/problem/19960

题意:

有一棵N个节点的树(1<=N<=10000),N-1条边,边的编号为1~N-1,每条边有一个权值,要求模拟两种操作:

1:DIST a b :求 点a到点b之间的距离

2:KTH a b k :求从a出发到b遇到的第k个节点的编号

QTREE系列的第二题。求dist就不用说啦,主要是求第k个。

方法一 :我是先跳了一遍,求出x到y的距离l,然后用树链剖分的跳法x走了k或者y走了l-k找到该点。很多细节。。。

方法二:先跳一遍,找到lca,然后判断k在x到lca的路上还是y到lca的路上,即是x到lca的第k个点或y到lca的第k‘个点。然后用倍增找到该点。(我觉得这个是最优的)

方法三:跳一遍找到lca后一层一层往上跳。。为什么这个方法不会超时。。TAT

方法一

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; const int N=;
char s[];
struct trnode{
int lc,rc,l,r,c;
}t[*N];
struct node{
int x,y,d,next;
}a[*N],b[N];
int n,tl,z,len;
int first[N],tot[N],son[N],fa[N],dep[N],ys[N],yss[N],top[N]; int maxx(int x,int y){return x>y ? x:y;} void ins(int x,int y,int d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=first[x];first[x]=len;
} int build_tree(int l,int r)
{
int x=++tl;
t[x].l=l;t[x].r=r;t[x].c=;
t[x].lc=t[x].rc=-;
if(l<r)
{
int mid=(l+r)>>;
t[x].lc=build_tree(l,mid);
t[x].rc=build_tree(mid+,r);
}
return x;
} void change(int x,int p,int c)
{
if(t[x].l==t[x].r) {t[x].c+=c;return;}
int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)>>;
if(p<=mid) change(lc,p,c);
else change(rc,p,c);
t[x].c=t[lc].c+t[rc].c;
} int query(int x,int l,int r)
{
if(t[x].l==l && t[x].r==r) return t[x].c;
int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)>>;
if(r<=mid) return query(lc,l,r);
else if(l>mid) return query(rc,l,r);
return query(lc,l,mid)+query(rc,mid+,r);
} void dfs1(int x)
{
tot[x]=;son[x]=;
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa[x]) continue;
fa[y]=x;
dep[y]=dep[x]+;
dfs1(y);
if(tot[son[x]]<tot[y]) son[x]=y;
tot[x]+=tot[y];
}
} void dfs2(int x,int tp)
{
ys[x]=++z;yss[z]=x;top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i;i=a[i].next)
{
int y=a[i].y;
if(y==fa[x] || y==son[x]) continue;
dfs2(y,y);
}
} int solve(int x,int y,int k)
{
int tx=top[x],ty=top[y];
int ans=,l=,xx=x,yy=y;
bool bk=;
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(tx,ty),swap(x,y);
if(!k) ans+=query(,ys[ty],ys[y]);
else l+=ys[y]-ys[ty]+;
y=fa[ty];ty=top[y];
} if(x==y) {l++;if(!k) return ans;}
else
{
if(dep[x]>dep[y]) swap(x,y);
l+=ys[y]-ys[x]+;
if(!k) return ans+query(,ys[son[x]],ys[y]);
}
//找第k个 debug!注意细节!
if(k>l) return ;
x=xx,y=yy;tx=top[x],ty=top[y];
int now1=,now2=,p=;
if(now1==k) return x;
if(now2==l-k+) return y;
while(tx!=ty)
{
if(dep[tx]>dep[ty]) swap(tx,ty),swap(x,y),p=-p;
int ll=ys[y]-ys[ty];
if(p)
{
if(now1+ll>=k) return ans=yss[ys[y]-(k-now1)];
else now1+=ll;
now1++;if(now1==k) return ans=fa[ty];
}
else
{
if(now2+ll>=l-k+) return ans=yss[ys[y]-(l-k+-now2)];
else now2+=ll;
now2++;if(now2==l-k+) return ans=fa[ty];
}
y=fa[ty];ty=top[y];
}
if(dep[x]>dep[y]) swap(x,y),p=-p;
if(p) ans=yss[ys[y]-(k-now1)];
else ans=yss[ys[y]-(l-k+-now2)];
return ans;
} int main()
{
freopen("a.in","r",stdin);
// freopen("me.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
len=;tl=;z=;dep[]=;tot[]=;
memset(fa,,sizeof(fa));
memset(first,,sizeof(first));
for(int i=;i<n;i++)
{
scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].d);
ins(b[i].x,b[i].y,b[i].d);
ins(b[i].y,b[i].x,b[i].d);
}
dfs1();
dfs2(,);
build_tree(,z);
for(int i=;i<n;i++) if(dep[b[i].x]>dep[b[i].y]) swap(b[i].x,b[i].y);
for(int i=;i<n;i++) change(,ys[b[i].y],b[i].d);
while()
{
scanf("%s",s);
int x,y,k=;
if((s[]=='D' && s[]=='I') || s[]=='K')
{
scanf("%d%d",&x,&y);
if(s[]=='K') scanf("%d",&k);
printf("%d\n",solve(x,y,k));
}
if(s[]=='D' && s[]=='O') break;
}
}
return ;
}

方法三

 #include<cstdio>
#include<cstring>
#define maxn 11000
using namespace std;
struct enode{int x,y,next;}a[maxn*];int len,last[maxn];
void ins(int x,int y)
{
len++; a[len].x=x; a[len].y=y;
a[len].next=last[x]; last[x]=len;
}
struct trnode{int lc,rc,l,r,c;}tr[maxn*];int trlen;
void bt(int l,int r)
{
int now=++trlen;
tr[now].l=l; tr[now].r=r;tr[now].lc=tr[now].rc=-;
tr[now].c=;
if(l<r)
{
int mid=(l+r)/;
tr[now].lc=trlen+; bt(l,mid);
tr[now].rc=trlen+; bt(mid+,r);
}
}
int n,fa[maxn],dep[maxn], son[maxn],tot[maxn],top[maxn];
void pre_tree_node(int x)
{
tot[x]=;son[x]=;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x])
{
fa[y]=x;
dep[y]=dep[x]+;
pre_tree_node(y);
tot[x]+=tot[y];
if(tot[son[x]]<tot[y]) son[x]=y;
}
}
} int z,ys[maxn];
void pre_tree_edge(int x,int tp)
{
ys[x]=++z;top[x]=tp;
if(son[x]!=)pre_tree_edge(son[x],tp);
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=fa[x]&& y!=son[x]) pre_tree_edge(y,y);
}
} void change(int now,int p,int c)
{
if( tr[now].l==tr[now].r) { tr[now].c=c; return ;}
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/;
if(p<=mid) change(lc,p,c); else change(rc,p,c);
tr[now].c=tr[lc].c+tr[rc].c;
}
int findsum(int now,int l,int r)//findsum的功能就是求新编号为l的边到新编号为r的边的总和(连续)
{
if( l==tr[now].l && tr[now].r==r) return tr[now].c;
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/;
if(mid<l) return findsum(rc,l,r);
else if(r<=mid) return findsum(lc,l,r);
else return findsum(lc,l,mid)+findsum(rc,mid+,r);
} int solve(int x,int y)
{
int tx=top[x],ty=top[y],ans=;
while(tx!=ty)
{
if(dep[tx]>dep[ty]){ int t=tx;tx=ty;ty=t; t=x;x=y;y=t;}
ans+= findsum(,ys[ty],ys[y]);
y=fa[ty];ty=top[y];
}
if(x==y) return ans;
else
{
if(dep[x]>dep[y]){ int t=x;x=y;y=t;}
return ans+ findsum(,ys[son[x]],ys[y]);
}
} int listx[maxn],listy[maxn]; //x一层层往上跳,经过的点保存在listx数组中
//y一层层往上跳,经过的点保存在listy数组中
int findKth(int x,int y,int K)//求从x点出发到y点,一路上遇到的第K个点是谁
{ //总体思路就是x和y一层层往上跳,比solve好理解啊。
int lx=,ly=,fx,fy;
while(x!=y)//如果x和y没有相遇
{
if(dep[x]>dep[y]){ listx[++lx]=x;x=fa[x];}//这里决定谁往上跳,为什么是不比tx和ty?
else { listy[++ly]=y;y=fa[y];} if(lx==K) return listx[lx]; //如果提前遇到第K个就直接结束了
}
listx[++lx]=x; // 此时x==y,随便listx或者listy都可以保存 if(K<=lx) return listx[K];
else return listy[ ly - (K-lx)+ ];
}
struct bian{int x,y,c;}e[maxn];
int main()
{
int i,tt,x,y,c,p,K; scanf("%d",&tt);
while(tt--)
{
scanf("%d",&n);
len=;memset(last,,sizeof(last));
for(i=;i<n;i++)
{
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
ins(e[i].x,e[i].y);
ins(e[i].y,e[i].x);
} dep[]=fa[]=; pre_tree_node(); z=; pre_tree_edge(,); trlen=;bt(,z); for(i=;i<n;i++) if( dep[e[i].x]>dep[e[i].y]){ int t=e[i].x;e[i].x=e[i].y;e[i].y=t;}
for(i=;i<n;i++) change(,ys[ e[i].y ], e[i].c); char ss[];
while( scanf("%s",ss)!=EOF)
{
if(ss[]=='O') break;
if(ss[]=='I'){scanf("%d%d",&x,&y); printf("%d\n",solve(x,y));}
else{ scanf("%d%d%d",&x,&y,&K); printf("%d\n",findKth(x,y,K));}
} }
return ;
}

【SPOJ - QTREE2】树链剖分的更多相关文章

  1. SPOJ 375 树链剖分

    SPOJ太慢了,SPOJ太慢了, 题意:给定n(n<=10000)个节点的树,每条边有边权,有两种操作:1.修改某条变的边权:2.查询u,v之间路径上的最大边权. 分析:树链剖分入门题,看这里: ...

  2. SPOJ QTREE 树链剖分

    树链剖分的第一题,易懂,注意这里是边. #include<queue> #include<stack> #include<cmath> #include<cs ...

  3. SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...

  4. SPOJ 375 (树链剖分+线段树)

    题意:一棵包含N 个结点的树,每条边都有一个权值,要求模拟两种操作:(1)改变某条边的权值,(2)询问U,V 之间的路径中权值最大的边. 思路:最近比赛总是看到有树链剖分的题目,就看了论文,做了这题, ...

  5. SPOJ 375 树链剖分 QTREE - Query on a tree

    人生第一道树链剖分的题目,其实树链剖分并不是特别难. 思想就是把树剖成一些轻链和重链,轻链比较少可以直接修改,重链比较长,用线段树去维护. 貌似大家都是从这篇博客上学的. #include <c ...

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

    You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  7. spoj 375 树链剖分模板

    /* 只是一道树链刨分的入门题,作为模板用. */ #include<stdio.h> #include<string.h> #include<iostream> ...

  8. 【学术篇】SPOJ QTREE 树链剖分

    发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能 ...

  9. spoj 375 树链剖分 模板

    QTREE - Query on a tree #tree You are given a tree (an acyclic undirected connected graph) with N no ...

随机推荐

  1. LeetCode:5. Longest Palindromic Substring(Medium)

    原题链接:https://leetcode.com/problems/longest-palindromic-substring/description/ 1. 题目要求:找出字符串中的最大回文子串 ...

  2. FPGA的嵌入式乘法器

    1. FPGA主要应用在并行处理资源的应用,视频与图像处理,无线通信的中频调制解调器. 嵌入式乘法器可以配置成一个 18 × 18 乘法器,或者配置成两个 9 × 9 乘法器.对于那些大于18 × 1 ...

  3. GreenMail邮件测试服务器

    GreenMail邮件测试服务器 http://blog.csdn.net/jackiehff/article/details/8741988 这个目前没有需求,所以暂不研究

  4. Maven初步接触

    最近随着搜资料,网上这样的字眼越来越多,我了解到这是构建项目的一种方式,于是准备简单看一下 首先粘几篇文章,作为学习的初步资料 Maven入门 http://blog.csdn.net/prstaxy ...

  5. vs code 在终端下使用 code ./ 打开当前项目

    Mac OS Visual Studio Code的扩展工具菜单中有Install command line的快捷安装 运行 VS code并打开命令面板( ⇧⌘P ),然后输入 shell comm ...

  6. tensorflow的几种优化器

    最近自己用CNN跑了下MINIST,准确率很低(迭代过程中),跑了几个epoch,我就直接stop了,感觉哪有问题,随即排查了下,同时查阅了网上其他人的blog,并没有发现什么问题 之后copy了一篇 ...

  7. Cassandra 常见错误索引

    类型错误 类型错误调试的技巧 有时候,类型错误提示比较不友好,比如不知道哪个字段出错. 在php中可以用 //过滤几个数据进行操作,逐个检查,或者折半查找错误 $data = array_splice ...

  8. 核方法(Kernel Methods)

    核方法(Kernel Methods) 支持向量机(SVM)是机器学习中一个常见的算法,通过最大间隔的思想去求解一个优化问题,得到一个分类超平面.对于非线性问题,则是通过引入核函数,对特征进行映射(通 ...

  9. Browser-Solidity的本地安装及使用介绍

    Browser-Solidity的本地安装及使用介绍 正所谓工欲善其事必先利其器,巧妇也难为无米之炊,所以在学习智能合约之前,必须要先把工具准备好.Browser-Solidity 是 Ethereu ...

  10. BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)

    原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面): Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力 ...