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. python 产生有序整数序列

    其中一种方法 A = np.linspace(0,n,n)

  2. MySQL数据库服务器逐渐变慢分析

    第一步 检查系统的状态 1.1 使用sar来检查操作系统是否存在IO问题 #sar -u 2 10 — 即每隔2秒检察一次,共执行20次. [root@CacheMemCache tester]# s ...

  3. SharePoint2013修复报错

         今天项目组的Sharepoint2013不小心被卸载了,本想着直接修复,谁知道在修复的时候一直报错,说找不到什么文件.报的就是类似于这样的错误: Product: ######### -- ...

  4. Memcached Hash算法

    本文来自网易云社区 作者:吕宗胜 Hash算法 1. Memcached Hash介绍 我们在前面的文章中已经介绍过了Memcached的内存管理方式,LRU的策略.由于Memcached的数据存储方 ...

  5. 『JavaScript』核心

    弱类型语言 JavaScript是一种弱类型的语言.变量可以根据所赋的值改变类型.原始类型之间也可以进行类型转换.其弱类型的物质为其带来了极大的灵活性. 注意:原始类型使用值传递,复合类型使用引用传递 ...

  6. Python|花了一天,为大家整理的一套来自外国大佬的密码速查表

    简单的HTTPS服务器 检查证书信息 输出 生成自签名证书 输出 准备一个签名注册请求 输出 生成无密码的RSA秘钥文件 用一个私钥给文件签名 输出 从签名验证一个文件 输出 通过pem文件做RSA加 ...

  7. js/jquery 获取本地文件的文件路劲 获取input框中type=‘file’ 中的文件路径(转载)

     原文:http://blog.csdn.net/niyingxunzong/article/details/16989947 js/jquery 获取本地文件的文件路劲 获取input框中type= ...

  8. STL应用——UVA673(堆栈)

    分析:栈的应用,遇到右括号便弹出栈顶元素,看是否与右括号相互匹配,其余情况压入栈. 注意:本题有坑,空串空串,为此我跪了数次 #include<iostream> #include< ...

  9. [剑指Offer] 36.两个链表的第一个公共结点

    题目描述 输入两个链表,找出它们的第一个公共结点. [思路]找出两个链表的长度,然后让长的走两个链表的长度差,然后再一起走(因为两个链表用公共的尾部). /* struct ListNode { in ...

  10. appendTo()方法和append()方法

    appendTo() 方法在被选元素的结尾(仍然在内部)插入指定内容.