【SPOJ - QTREE2】树链剖分
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】树链剖分的更多相关文章
- SPOJ 375 树链剖分
SPOJ太慢了,SPOJ太慢了, 题意:给定n(n<=10000)个节点的树,每条边有边权,有两种操作:1.修改某条变的边权:2.查询u,v之间路径上的最大边权. 分析:树链剖分入门题,看这里: ...
- SPOJ QTREE 树链剖分
树链剖分的第一题,易懂,注意这里是边. #include<queue> #include<stack> #include<cmath> #include<cs ...
- SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)
题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...
- SPOJ 375 (树链剖分+线段树)
题意:一棵包含N 个结点的树,每条边都有一个权值,要求模拟两种操作:(1)改变某条边的权值,(2)询问U,V 之间的路径中权值最大的边. 思路:最近比赛总是看到有树链剖分的题目,就看了论文,做了这题, ...
- SPOJ 375 树链剖分 QTREE - Query on a tree
人生第一道树链剖分的题目,其实树链剖分并不是特别难. 思想就是把树剖成一些轻链和重链,轻链比较少可以直接修改,重链比较长,用线段树去维护. 貌似大家都是从这篇博客上学的. #include <c ...
- 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, ...
- spoj 375 树链剖分模板
/* 只是一道树链刨分的入门题,作为模板用. */ #include<stdio.h> #include<string.h> #include<iostream> ...
- 【学术篇】SPOJ QTREE 树链剖分
发现链剖这东西好久不写想一遍写对是有难度的.. 果然是熟能生巧吧.. WC的dalao们都回来了 然后就用WC的毒瘤题荼毒了我们一波, 本来想打个T1 44分暴力 然后好像是特判写挂了还是怎么的就只能 ...
- spoj 375 树链剖分 模板
QTREE - Query on a tree #tree You are given a tree (an acyclic undirected connected graph) with N no ...
随机推荐
- ArrayMap java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[]
错误堆栈: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Object[] at android ...
- Koa基本使用
简介 koa 是由 Express 原班人马打造的,致力于成为一个更小.更富有表现力.更健壮的 Web 框架. 使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的 ...
- Eclipse AmaterasUML 安装及使用
AmaterasUML 对于我来说,是一个非常好用的UML插件. 用它来将我写过的一些Android程序进行逆工程非常好用,只不过,不能体现出包,这是一个小小的遗憾. 这个是它的主页地址:http:/ ...
- hdu5863 cjj's string game
矩阵快速幂 #include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int MOD = ...
- Wordpress 设置后台自定义post 排序
创建新的 Post type时,文章在后台默认使用 Titile 列进行升序排序,但是通常情况下我们需要按日期 Date 进行降序排序, function wpse_81939_post_types_ ...
- 1003 Emergency (25 分)(求最短路径)
给出N个城市,m条无向边.每个城市中都有一定数目的救援小组,所有边的边权已知.现在给出起点和终点,求从起点到终点的最短路径条数及最短经上的救缓小组数目只和.如果有多条最短路径,则输出数目只和最大的 D ...
- popen和system问题
popen和system问题 1. 问题描述 C的代码里面去调用命令启动一个shell脚本,分别使用了下面两个途径. 其中一个是: func1(cmd) { popen(cmd,type); pclo ...
- 编译 TensorFlow 的 C/C++ 接口
TensorFlow 的 Python 接口由于其方便性和实用性而大受欢迎,但实际应用中我们可能还需要其它编程语言的接口,本文将介绍如何编译 TensorFlow 的 C/C++ 接口. 安装环境: ...
- Halcon和Opencv区别
Halcon:机器视觉行业里知名的商业视觉库,非开源的,在国内市场份额处于第一,其提供了1500个多个API算子供开发人员使用,有些编程基础的都可以轻松的入门,其调试也是很方便的,断点单步运行,图像变 ...
- Hibernate 查询,返回结果设置到DTO
1:原生sql的查询,返回结果设置到DTO: Query query = sessionFactoryRtData.getCurrentSession().createSQLQuery(hql.toS ...