【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 ...
随机推荐
- 思杰VDI提示“The VDI is not available”
前言:困扰已久的问题终于解决. 问题:客户反馈无法连接VDI. 解决过程:1.登录后台查看VDI状态为关机状态尝试重新启动提示如下图: 2.判断此VDI的启动盘出现问题(注:本人环境无数据盘) 3.查 ...
- iOS中的数据库应用
iOS中的数据库应用 SLQLite简介 什么是SQLite SQLite是一款轻型的嵌入式数据库 它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了 它的处理速度比Mysql.Post ...
- Assetbundle2
Assetbundle可以将Prefab封装起来,这是多么方便啊! 而且我也强烈建议大家将Prefab封装成Assetbundle,因为Prefab可以将游戏对象身上带的游戏游戏组件.游戏脚本.材质都 ...
- 论文翻译 - Multiagent Bidirectionally-Coordinated Nets Emergence of Human-level Coordination in Learning to Play StarCraft Combat Games
(缺少一些公式的图或者效果图,评论区有惊喜) (个人学习这篇论文时进行的翻译[谷歌翻译,你懂的],如有侵权等,请告知) Multiagent Bidirectionally-Coordinated N ...
- UVa 294 - Divisors 解题报告 c语言实现 素数筛法
1.题目大意: 输入两个整数L.H其中($1≤L≤H≤10^9,H−L≤10000$),统计[L,H]区间上正约数最多的那个数P(如有多个,取最小值)以及P的正约数的个数D. 2.原理: 对于任意的一 ...
- final 内部类 static
[Java中为什么会有final变量]: final这个关键字的含义是“这是无法改变的”或者“终态的”: 那么为什么要阻止改变呢? java语言的发明者可能由于两个目的而阻止改变: 1).效率问题: ...
- python完成简单购物功能
# # -*- coding: utf8 -*- # # Author:wxq # # date:2017/11/13 # # python 3.6 # 创建一个商品列表: product_lis = ...
- # ML学习小笔记—Gradien Descent
关于本课程的相关资料http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17.html 根据前面所为,当我们得到Loss方程的时候,我们希望求得最优的Loss方 ...
- Spring温故而知新 – bean的装配
Spring装配机制 Spring提供了三种主要的装配机制: 1:通过XML进行显示配置 2:通过Java代码显示配置 3:自动化装配 自动化装配 Spring中IOC容器分两个步骤来完成自动化装配: ...
- PAT 甲级 1036 Boys vs Girls(20)
https://pintia.cn/problem-sets/994805342720868352/problems/994805453203030016 This time you are aske ...