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. cmd中可以运行java,但不能运行javac命令

    在cmd中可以运行java,但运行javac命令时提示:'javac' 不是内部或外部命令,也不是可运行的程序或批处理文件. 原因:安装java时把jdk的路径和jre的路径选择成一样,就造成覆盖了. ...

  2. Java输出日历写法

    package TestString_2; import java.text.ParseException;import java.util.Calendar;import java.util.Gre ...

  3. 数据迁移的应用场景与解决方案Hamal

    本文来自网易云社区 作者:马进 跑男热播,作为兄弟团忠实粉丝,笔者也是一到周五就如打鸡血乐不思蜀. 看着银幕中一众演员搞怪搞笑的浮夸演技,也时常感慨,这样一部看似简单真情流露的真人秀,必然饱含了许许多 ...

  4. 了解url

    我对自己知道关于url的编码和解码的一些进行了一下整理. 我们的例子是百度翻译的地址: http://fanyi.baidu.com/translate#en/zh/The%20%22%3F%20ar ...

  5. spring boot 连接mysql 错误The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one

    1.spring boot 整合mybatis 连接mysql时错误 The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or repr ...

  6. ZooKeeper完全分布式安装与配置

    Apache ZooKeeper是一个为分布式应用所设计开源协调服务,其设计目是为了减轻分布式应用程序所承担的协调任务.可以为用户提供同步.配置管理.分组和命名服务. 1.环境说明 在三台装有cent ...

  7. 【leetcode】19. 删除链表的倒数第N个节点

    描述 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变 ...

  8. 21天学习caffe(二)

    本文大致记录使用caffe的一次完整流程 Process 1 下载mnist数据集(数据量很小),解压放在data/mnist文件夹中:2 运行create_mnist.sh,生成lmdb格式的数据( ...

  9. 一个简单的ipfs音乐播放器的实现

    IPFS音乐播放器 IPFS相关 IPFS第一次亲密接触 什么是IPFS IPFS对比HTTP/FTP等协议的优势 IPFS应用场景 -移动数据 交易 路由 网络 定义数据 命名 使用数据 具体场景; ...

  10. java设计模式之观察者模式以及在java中作用

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...