装逼的英语解释了好多意义:
RMQ是基础:Range Max/Min Query:查找区间的最大或者最小的算法:见刘汝佳训练指南P197-P198
思想:dp【i】【j】表示从i开始的,长度为2^j的一段元素的最小值
例题:网络赛签到题:HDOJ5443TheWaterProblem
LCA:lowest common ancestor:最近公共祖先
http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html
http://ayzk.wordpress.com.cn/archives/14
例题:
POJ 1330 bin神模板过之
HDOJ 2586
变化:题中树上边有权值
代码改动:
《1》再定义一个数组,dir【】表示该点离根的距离
《2》addedge在加边时需要多添加一个参数:两点距离
《3》lca求出来的是位置坐标
《4》在dfs中需要记录节点离根的距离即:dir【v】=dir【u】+edge【】。cost
《5》最后答案ans=dir【u】+dir【v】-dir【lca】:自己画图就明白了的
倍增法:二进制压缩思路:http://blog.csdn.net/lanshui_yang/article/details/11746513
模板就是这么666
贴上我bin的模板:可以裸过POJ 1330
int rmq[2*maxn];
struct ST{
int mm[2*maxn];
int dp[2*maxn][20];
void init(int n){
mm[0]=-1;
for(int i=1;i<=n;i++){
mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
dp[i][0]=i;
}
for(int j=1;j<=mm[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=rmq[dp[i][j-1]]<rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
}
int query(int a,int b){
if (a>b) swap(a,b);
int k=mm[b-a+1];
return rmq[dp[a][k]]<=rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
}
};
struct Edge{
int to,Next;
}edge[maxn*2];
int tot,head[maxn];
int F[maxn*2];
int P[maxn];
int cnt;
ST st;
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].Next=head[u];
head[u]=tot++;
}
void dfs(int u,int pre,int dep){
F[++cnt]=u;
rmq[cnt]=dep;
P[u]=cnt;
for(int i=head[u];i!=-1;i=edge[i].Next){
int v=edge[i].to;
if (v==pre) continue;
dfs(v,u,dep+1);
F[++cnt]=u;
rmq[cnt]=dep;
}
}
void LCA_init(int root,int node_num){
cnt=0;
dfs(root,root,0);
st.init(2*node_num-1);
}
int query_lca(int u,int v){
return F[st.query(P[u],P[v])];
}
bool flag[maxn];
int main(){
//input;
int T,N,u,v;
scanf("%d",&T);
while(T--){
scanf("%d",&N);
init();
memset(flag,false,sizeof(flag));
for(int i=1;i<N;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
flag[v]=true;
}
int root;
for(int i=1;i<=N;i++)
if (!flag[i]){
root=i;
break;
}
LCA_init(root,N);
scanf("%d%d",&u,&v);
printf("%d\n",query_lca(u,v));
}
return 0;
}