码迷,mamicode.com
首页 > 其他好文 > 详细

学习 LCA&&RMQ

时间:2015-02-13 10:11:09      阅读:167      评论:0      收藏:0      [点我收藏+]

标签:lca   rmq   

参考:点击打开链接 点击打开链接      点击打开链接(一些总结) 点击打开链接(不错的模板)

题目:点击打开链接

花了4天时间做完了这个专题,LCA的问题用处还是很大,同时能体会RMQ的ST算法中dp的味道.基本方法就是ST,LCA转RMQ,LCA的Tarjan,LCA倍增(这个可存储边权)

这个专题后面四道题都非常好,推荐大家做做.

细节:

1. ST方法2^i 包含自己,因此其真实只包含到i+2^k-1的范围.

2. Tarjan一般都很快,但不适合修改类型的问题,关于权值长度之类的,ST就不能用了.比如E

3. LCA转RMQ记得有三个数组,长度为2*N-1 ,很容易写错,然后会奇怪的报TLE错误.rmq里面存放的是下标.........

4.关于树遍历判重,不是par就行,可以不用vis,要add(a,b) add(b,a)不然会冲突顶死.E题就犯这个错误了.

模板:

void ST(int x){
    int LOG=log(x+0.0)/log(2.0);
    for(int i=1;i<=x;i++) rmq[0][i]=i;
    for(int k=1;k<=LOG;k++)
        for(int i=1;i+(1<<k)-1<=x;i++){/// -1这里要注意
            int a=rmq[k-1][i],b=rmq[k-1][i+(1<<k-1)];
            if(dep[a]<dep[b]) rmq[k][i]=a;
            else rmq[k][i]=b;
    }
}
int RMQ(int a,int b){
    if(a>b) swap(a,b);
    int LOG=log(b-a+1.)/log(2.0);
    int c=rmq[LOG][a],d=rmq[LOG][b-(1<<LOG)+1];///+1这里要注意
    if(dep[c]<dep[d]) return c;
    else return d;
}


void Tarjan(int u){
    vis[u]=1;
    fa[u]=u;
    for(int i=head[u];~i;i=edge[i].nxt) if(!vis[edge[i].v]){
        int v=edge[i].v;
        whead[v]=whead[u]+edge[i].w;
        Tarjan(v);
        fa[v]=u;
    }
    for(int i=qhead[u];~i;i=query[i].nxt) if(vis[query[i].v]){
        int v=query[i].v;
        query[i].w=query[i^1].w=whead[u]+whead[v]-2*whead[Find(v)];///这里找祖先
    }
}
A - A Magic Lamp

从里面选几个数字使其最小,裸的RMQ  HDU3183

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<string.h>
using namespace std;
int dp[20][1024];
char num[1024],hash[1024];
int min( int i,int j )
{
   return num[i]<=num[j]?i:j;
}
void RMQ( int n )
{
    for( int i=0; i< n ;i++ )
       dp[0][i]=i;
    int t=( int  )( log( double( n ) )/log( 2.0 ) );
    for( int j=1; j<=t ; j++ )
       for( int i=0; i+( 1<<j )-1<n; i++ )
         dp[j][i]=min( dp[j-1][i],dp[j-1][i+( 1<<( j-1 ) )] );
}
int result( int left,int right )
{
   int k=( int )( log( double( right-left+1 ) )/log( 2.0 ) );
   return min( dp[k][left],dp[k][right-( 1<<k )+1] );
}
int main( )
{
    int n;
   while( scanf( "%s%d",num,&n )!=EOF ){
     int len=strlen( num );
     RMQ( len );
     int N=len-n;
     int i=0,j=0;
     while( N-- ){
       i=result( i,len-N-1 );
       hash[j++]=num[i++];
     }
     for( i=0;i<j;i++ ) if( hash[i]!='0' )
           break;
      if( i==j ) {printf( "0" );continue;}
         while( i<j ){
           printf( "%c" , hash[i] );
           i++;
         }
      puts( "" );
    }
   return 0;
}
B - Interviewe
HDU3486 直接枚举会超时,且并不符合二分的性质.自己慢慢优化吧.其实没什么意思.

C - Nearest Common Ancestors

POJ1330 LCA转RMQ问题,模板题

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 2e4+10;
int N,M,T;
int nxt[MAXN],v[MAXN],head[MAXN],idx[2*MAXN],id,tot;
int dep[MAXN],pos[MAXN],in[MAXN];
int rmq[MAXN][20];
int min( int i,int j )
{
   return dep[i]<=dep[j]?i:j;
}
void init(){
    id=0;
    tot=0;
    memset(in,0,sizeof(in));
    memset(head,-1,sizeof(head));
}
void add(int a,int b){
    v[tot]=b;
    nxt[tot]=head[a];
    head[a]=tot++;
}
void dfs(int u,int d){
    idx[id]=u;
    dep[id]=d;
    pos[u]=id++;
    for(int i=head[u];~i;i=nxt[i]){
        dfs(v[i],d+1);
        idx[id]=u;
        dep[id++]=d;
    }
}
void RMQ(){
    for(int i=0;i<id;i++) rmq[i][0]=i;
    int K=log(id)/log(2.0);
    for(int k=1;k<=K;k++)
        for(int i=0;i+(1<<k)-1<id;i++)
            rmq[i][k]=min(rmq[i][k-1],rmq[i+(1<<(k-1))][k-1]);
}
int Q(int a,int b){
    if(a>b) swap(a,b);
    int K=log(b-a+1)/log(2.0);
    if(dep[min(rmq[a][K],rmq[b-(1<<K)+1][K])]==dep[rmq[a][K]])
        return rmq[a][K];
    return rmq[b-(1<<K)+1][K];
}

int main(){
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d",&N);
        for(int i=1;i<N;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);
            in[b]++;
        }
        for(int i=1;i<=N;i++) if( !in[i]){
            dfs(i,1);
            break;
        }
        RMQ();
        int a,b;scanf("%d%d",&a,&b);
        printf("%d\n",idx[Q(pos[a],pos[b])]);
    }
    return 0;
}

D - Closest Common Ancestors

POJ1470 同模板题,不过输入输出要注意一下,网上有好方法.

while(getchar()!='(');
scanf("%d %d",&a,&b);
while(getchar()!=')');

E - Distance Queries

POJ1986 两点间距离.都求出到根的距离,然后dist[u]+dist[v]-2*dist[lca]就可以了.

query[i].w=query[i^1].w=whead[u]+whead[v]-2*whead[Find(v)];///这里找祖先
F - How far away ?
和E差不多.HDU2586

G - Design the city

ZOJ3195 题意:给一个无根树,有q个询问,每个询问3个点,问将这3个点连起来,距离最短是多少,

LCA的模板题,分别求LCA(X,Y),LCA(X,Z),LCA(Y,Z),和对应的距离,然后3个距离相加再除以2就是这个询问的结果

H - Connections between cities

模板题太多HDU2874

I - Network

HDU3078 这个题按照题意做就行,不会超时.因为有修改点权值的操作,所以不能用倍增的方法存储最优解.

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 100000+10;
int N,M,K,T;
struct P{
    int to,nxt;
}edge[2*MAXN];
int head[MAXN],tot;
int num[MAXN],vis[MAXN];
int ans[MAXN];
int rmq[30][2*MAXN],dep[2*MAXN],pos[MAXN],seq[2*MAXN],id;///
int pre[MAXN];
bool cmp(int a, int b)
{
    return a > b;
}
void init(){
    tot=id=0;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    memset(head,-1,sizeof(head));
}
void add(int a,int b){
    edge[tot].to=b;
    edge[tot].nxt=head[a];
    head[a]=tot++;
}
void RMQ(){
    int K=log(id+0.0)/log(2.0);
    for(int i=1;i<id;i++) rmq[0][i]=i;
    for(int i=1;i<=K;i++)
        for(int j=1;j+(1<<i)-1<id;j++)
            if(dep[ rmq[i-1][j] ]<dep[ rmq[i-1][j+(1<<(i-1))] ])
                rmq[i][j]=rmq[i-1][j];
            else rmq[i][j]=rmq[i-1][j+(1<<(i-1))];
}
int Q(int a,int b){
    if(a>b) swap(a,b);
    int K=log(b-a+1.0)/log(2.0);
    if(dep[ rmq[K][a] ]<dep[ rmq[K][b-(1<<K)+1] ])///
        return rmq[K][a];
    else return rmq[K][b-(1<<K)+1];
}
void dfs(int u,int d){
    vis[u]=1;
    seq[id]=u;
    dep[id]=d;
    pos[u]=id++;
    for(int i=head[u];~i;i=edge[i].nxt) if(!vis[edge[i].to]){
        pre[edge[i].to]=u;
        dfs(edge[i].to,d+1);
        seq[id]=u;
        dep[id++]=d;
    }
}
int main(){
    scanf("%d%d",&N,&M);
    init();
    for(int i=1;i<=N;i++)
        scanf("%d",num+i);
    for(int i=1;i<N;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(1,1);RMQ();
    for(int i=0;i<M;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(a==0) {num[b]=c;continue;}
        int d=seq[Q(pos[b],pos[c])];
        int cnt=0;
        for(int i=b;i!=d;i=pre[i])
            ans[cnt++]=num[i];
        for(int i=c;i!=d;i=pre[i])
            ans[cnt++]=num[i];
        ans[cnt++]=num[d];
        sort(ans,ans+cnt,cmp);
        if(cnt<a) puts("invalid request!");
        else printf("%d\n",ans[a-1]);
    }
    return 0;
}
J - Housewife Wind
poj2763  好题!用树状数组优化加速.这里对于欧拉数组,有个特点改变一个点或者该点父边.则受影响的孩子都在欧拉数组中间.first为第一次访问点,second为结束访问点.若有改变

就add(first[u],x),add(second[u]+1,-x) 求和就是sum(x)+sum(y)-sum(lca);

K - Network

poj3694  和AC程序对拍了很多数据都一样但是RE,懒得找问题了.

用Tarjan缩点

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 400000+10;
int N,M,S,K,T;
struct P{
    int st,to,w,nxt;
}edge[2*MAXN];
int vis[MAXN],head[MAXN],tot,idx;
int dep[MAXN],fa[MAXN],parent[MAXN];
int dfn[MAXN],low[MAXN],isbridge[MAXN],bcnt;
int mystack[MAXN],top,instack[MAXN];
int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
void init(){
    tot=bcnt=idx=top=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(fa,-1,sizeof(fa));
    memset(isbridge,0,sizeof(isbridge));
}
void add(int a,int b){
    edge[tot].st=a;
    edge[tot].to=b;
    edge[tot].nxt=head[a];
    head[a]=tot++;
}

void Tarjan(int u,int d){
    dfn[u]=low[u]=idx++;
    mystack[top++]=u;
    vis[u]=1;
    dep[u]=d;
    for(int i=head[u];~i;i=edge[i].nxt) {
        int v=edge[i].to;
        if(!vis[v]){
            parent[v]=u;///回溯上去
            Tarjan(v,d+1);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<low[v]) bcnt++,isbridge[v]=1;///点的父边代表其
        }
        else if(vis[v] == 1) low[u]=min(low[u],dfn[v]);/// double edge ignore it 若本身有反向边就不存在这个了
    }
    if(dfn[u]==low[u]){
        int k;
        do{
            k=mystack[--top];
            fa[k]=u;
        }while(top && k!=u);
    }
    vis[u]=2;
}
void LCA(int a,int b){
    if(dfn[a]<dfn[b]) swap(a,b);
    int c=Find(a),d=Find(b);
    if(c==d && c!=-1) return;
    while(dep[a]>dep[b]){
        if(isbridge[a]) bcnt--;
        isbridge[a]=0;
        a=parent[a];
    }
    while(a!=b){
        if(isbridge[a]) bcnt--;
        if(isbridge[b]) bcnt--;
        isbridge[a]=isbridge[b]=0;
        a=parent[a];b=parent[b];
    }
    fa[c]=d;
}
int main(){
    int q,cas=0;
    while(~scanf("%d%d",&N,&M)){
        if(N==0 && M==0) break;
        init();
        for(int i=0;i<M;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        Tarjan(1,1);
        printf("Case %d:\n",++cas);
        scanf("%d",&q);
        for(int i=0;i<q;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            LCA(u,v);
            printf("%d\n",bcnt);
        }
        puts("");
    }
    return 0;
}
L - Network

poj3417 好题,减掉一条原来就有的边,减掉一条增加的边,问有几种方法可以让图变成两部分.

想一下就知道若是树的话每条都是关键的,那么新边随便减 M

若被环覆盖一次,只要减去构成环的那条边,1种方法,

若>1则没用``````

思路是这个,但是实现很有技巧,dp[u]表示被环覆盖几次. 对一条新边(u,v),则有dp[u]++,dp[v]++ ,dp[lca]-=2

最后在dfs一次累加上去就OK.dp[u]+=dp[v]

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 100000+10;
int N,M,S,K,T;
struct P{
    int st,to,nxt;
}edge[2*MAXN];
int head[MAXN],dep[MAXN<<1],idx[MAXN<<1],first[MAXN],id,tot;
int vis[MAXN],dp[MAXN];
int rmq[30][MAXN<<1];

void init(){
    tot=id=0;
    memset(dp,0,sizeof(dp));
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
}
void ST(int x){
    int LOG=log(x+0.0)/log(2.0);
    for(int i=1;i<=x;i++) rmq[0][i]=i;
    for(int k=1;k<=LOG;k++)
        for(int i=1;i+(1<<k)-1<=x;i++){/// -1
            int a=rmq[k-1][i],b=rmq[k-1][i+(1<<k-1)];
            if(dep[a]<dep[b]) rmq[k][i]=a;
            else rmq[k][i]=b;
    }
}
int RMQ(int a,int b){
    if(a>b) swap(a,b);
    int LOG=log(b-a+1.)/log(2.0);
    int c=rmq[LOG][a],d=rmq[LOG][b-(1<<LOG)+1];///+1
    if(dep[c]<dep[d]) return c;
    else return d;
}
void add(int a,int b){
    edge[tot].st=a;
    edge[tot].to=b;
    edge[tot].nxt=head[a];
    head[a]=tot++;
}
void dfs(int u,int d,int par){
    dep[++id]=d;
    idx[id]=u;first[u]=id;
    for(int i=head[u];~i;i=edge[i].nxt) if(edge[i].to !=par){
        dfs(edge[i].to,d+1,u);
        idx[++id]=u;
        dep[id]=d;
    }
}
int getans(int u,int par){
    int res=0;
    for(int i=head[u];~i;i=edge[i].nxt) if(edge[i].to!=par){
        res+=getans(edge[i].to,u);
        dp[u]+=dp[edge[i].to];
    }
    if(u==1 || dp[u]>=2) return res;
    if(dp[u]==0) return res+M;
    if(dp[u]==1) return res+1;
}

int main(){
    int q,cas=0;
    while(~scanf("%d%d",&N,&M)){
        init();
        for(int i=1;i<N;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        dfs(1,1,-1);ST(id);
        for(int i=0;i<M;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            int lca=idx[RMQ(first[u],first[v])];
            dp[u]++;dp[v]++;dp[lca]-=2;
        }
        
        printf("%d\n",getans(1,-1));
    }
    return 0;
}

M - The merchant

poj 3728 题意大致是从两个点间倒手货物,使得获利最大,求价值.

这个题暗含了方向的意思,买卖次序不能颠倒,我刚开始一直用倍增方法找最小最大,那么久分两种u--->lca--->v 那么三种情况

1. 在u--->lca过程中买卖完成    2. lca--->v 买卖完成   3.u--->lca 买   lca--->v 卖

那么使用Tarjan方法每次维护四个数组

mx u到lca最大值  mn u到lca最小值 up 从u到lca最大获利,  down 从lca到u最大获利.

那么一条路径(u,v) 的最大获利=max(up[u],down[u],mx[v],mn[u]); 最后面这个因为是低买高卖  

如何维护就类似于关系并查集了.并且要逐层回答,因为这几个数组网上情况会变,因此要将同lca的询问一起回答.

关系并查集就是我 和父亲的关系 父亲和祖先的关系 推导出我和祖先的关系,对应此题就是求所维护的数组中数最优.

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
#define long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 100000+10;
int N,M,S,K,T;

struct P{
    int st,to,nxt,id;
}edge[MAXN<<3];
int head[MAXN],head2[MAXN],head3[MAXN],id,tot;
int fa[MAXN],cost[MAXN],vis[MAXN];
int mx[MAXN],mn[MAXN],up[MAXN],down[MAXN];
int ans[MAXN];
int Find(int x){
    if(x==fa[x]) return x;
    int t=fa[x];
    fa[x]=Find(fa[x]);
    up[x]=max(up[t],max(up[x],mx[t]-mn[x]));///
    down[x]=max(down[t],max(down[x],mx[x]-mn[t]));
    mx[x]=max(mx[x],mx[t]);
    mn[x]=min(mn[x],mn[t]);
    return fa[x];
}
void init(){
    tot=id=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    memset(head3,-1,sizeof(head3));
}

void add(int a,int b,int c){
    edge[tot].id=c;
    edge[tot].st=a;
    edge[tot].to=b;
    edge[tot].nxt=head[a];
    head[a]=tot++;
}
void add_ask(int a,int b,int c){
    edge[tot].id=c;
    edge[tot].st=a;
    edge[tot].to=b;
    edge[tot].nxt=head2[a];
    head2[a]=tot++;
}
void Tarjan(int u,int par){
    fa[u]=u;vis[u]=1;
    for(int i=head[u];~i;i=edge[i].nxt) if(edge[i].to !=par){
        Tarjan(edge[i].to,u);
        fa[edge[i].to]=u;
    }
    for(int i=head2[u];~i;i=edge[i].nxt) if(vis[edge[i].to]){
        int t=Find(edge[i].to);
        edge[tot].id=i;
        edge[tot].nxt=head3[t];
        head3[t]=tot++;
    }///回溯只有逐层LCA才有效,利用根的唯一性.之前就是找到就回溯,WA
    for(int i=head3[u];~i;i=edge[i].nxt){
        int k=edge[i].id;
        int a=edge[k].st,b=edge[k].to,c=edge[k].id;
        if(ans[abs(c)]) continue;
        Find(a);
        if(c<0) swap(a,b),c=-c;
        ans[c]=max( up[a], max(down[b],mx[b]-mn[a]) );
    }
}
///
int main(){
    while(~scanf("%d",&N)){
        init();
        for(int i=1;i<=N;i++) scanf("%d",cost+i),mn[i]=mx[i]=cost[i],up[i]=down[i]=0;
        for(int i=1;i<N;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b,i);
            add(b,a,-i);
        }
        int q;
        scanf("%d",&q);
       for(int i=1;i<=q;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add_ask(a,b,i);
            add_ask(b,a,-i);
        }
        Tarjan(1,-1);
        for(int i=1;i<=q;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}


N - Checkers

HDU 3830  任何一个状态,通过题目所给的移动,都能对应且唯一对应一个b*2=a+c(a<b<c)的状态,这是突破点

其三个点合起来看做一个状态,至多三种转移状态.因此就很像二叉树,并且根的状态唯一,左右两边相等.所以以这个为根
如果不是根的状态,可以让左右两个往里跳,依据是让和中间那个坐标距离缩小
这样就可以看作为是向根移动 图并不用显示构造出来,用辗转相除的思想就可以解决,

然后二分+LCA.这里的二分注意,从点出发往根走,满足题意了才是减少步数,且不像一般的二分high=mid-1.因为这样很可能就过头了

#include<ctime>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-8
typedef long long ll;
#define lowbit(x) (x&-x)
const int MAXN = 100000+10;
int N,M,S,K,T;

typedef struct {
    ll a,b,c,dep;
}state;
state st,ed,srt,ert;
int head[MAXN],head2[MAXN],head3[MAXN],id,tot;
int fa[MAXN],cost[MAXN],vis[MAXN];
int ans[MAXN];

void mysort(state &x){
    if(x.a>x.b) swap(x.a,x.b);
    if(x.a>x.c) swap(x.a,x.c);
    if(x.b>x.c) swap(x.b,x.c);
}
bool cmp(state a,state b){
    if(a.a==b.a && a.b==b.b && a.c==b.c) return true;
    return false;
}
state findroot(state& x){
    ll dep=0;
    ll a=x.a,b=x.b,c=x.c,t;
    while(b-a!=c-b){
        ll len=b-a,_len=c-b;
        if(len>_len){
            t=(len-1)/_len;
            b-=t*_len;
            c-=t*_len;
        }
        else{
            t=(_len-1)/len;
            b+=t*len;
            a+=t*len;
        }
        dep+=t;
    }
    x.dep=dep;
    state res={a,b,c,0};
    return res;
}
state update(state x,ll delta){
    ll a=x.a,b=x.b,c=x.c,t;
    while(delta>0){
        ll len=b-a,_len=c-b;
        if(len>_len){
            t=(len-1)/_len;
            if(t>delta) t=delta;
            b-=t*_len;
            c-=t*_len;
        }
        else{
            t=(_len-1)/len;
            if(t>delta) t=delta;
            b+=t*len;
            a+=t*len;
        }
        delta-=t;
    }
    state res={a,b,c};
    return res;
}
int main(){

    while(~scanf("%I64d%I64d%I64d",&st.a,&st.b,&st.c)){
        scanf("%I64d%I64d%I64d",&ed.a,&ed.b,&ed.c);
        mysort(st);mysort(ed);
        srt=findroot(st);
        ert=findroot(ed);
        if(!cmp(srt,ert)){
            puts("NO");continue;
        }
        if(st.dep<ed.dep) swap(st,ed);
        ll ans=st.dep-ed.dep;
        st=update(st,ans);
        ll l=0,h=ed.dep;
        state t1,t2;
        while(l<h){
            ll mid=(l+h)>>1;
            t1=st,t2=ed;
            t1=update(st,mid);
            t2=update(ed,mid);
            if(cmp(t1,t2)) h=mid;///二分法起初多写了个-1,WA
            else l=mid+1;
        }
        printf("YES\n%I64d\n",ans+2*l);
    }
    return 0;
}


学习 LCA&&RMQ

标签:lca   rmq   

原文地址:http://blog.csdn.net/gg_gogoing/article/details/43791499

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!