【NOIp复习】最近公共祖先LCA&区间最大最小RMQ

RMQ问题的在线ST算法

VIJOS 1514

#include 
#include 
#include 
using namespace std;

int n,m,a,b,dp[201000][30],s[201000];

void init_RMQ(){
    for(int i=1;i<=n;i++) dp[i][0]=s[i];
    for(int j=1;(1<
            for(int i=1;i<=n;i++)
                dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    return;
}

int query_RMQ(int l,int r){
    int k=log(r-l+1)/log(2.0);
    return max(dp[l][k],dp[r-(1<
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&s[i]);
    init_RMQ();
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&a,&b);
        printf("%d\n",query_RMQ(a,b));
    }
    return 0;
}

//记住dp[i][0]=s[i]
//dp[i][j]=min/max{dp[i][j-1],dp[i+(1<][j-1]}
//设k=log2(r-l+1)
//要查询的结果为min/max{dp[l][k],dp[r-(1<][k]} 

Tarjan(离线求LCA)

#include 
#include 
#include 
#define maxn 50000

struct qu{
    int from,to;
}q[maxn];

int m,n,s,cntq=0;
int g[maxn][1000];
int fa[maxn],ancestor[maxn];
bool vis[maxn];

int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]);
}

void lca(int u,int p){
    ancestor[find(u)]=u;
    vis[u]=1;
    for(int i=1;i<=g[u][0];i++) if(g[u][i]!=p){
        lca(g[u][i],u);
        fa[find(g[u][i])]=find(u);
        ancestor[find(u)]=u;
    }
    for(int i=1;i<=cntq;i++){
        int qa=q[i].from,qb=q[i].to;
        if(qa==u&&vis[qb]) printf("%d\n",ancestor[find(qb)]);
        else if(qb==u&&vis[qa]) printf("%d\n",ancestor[find(qa)]);
    }
    return;
}

int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=n;i++) fa[i]=ancestor[i]=i;
    cntq=m;
    for(int i=1;iint x,y;
        scanf("%d%d",&x,&y);
        g[x][++g[x][0]]=y;
        g[y][++g[y][0]]=x;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].from,&q[i].to); 
    }
    lca(s,-1);
    return 0;
}

VIJOS 1460(我也不知道是不是Tarjan,反正dfs跑一遍就过了)

#include 
#include 
#include 
#define maxn 100100

int cnt,ans1=0,head[maxn],k,q;
long long dist[maxn],fa[maxn],ans2=0;

struct edge{
    int to,next,val;
}edges[maxn]; 

void add(int u,int v,int val){
    edges[++cnt].to=v;
    edges[cnt].val=val;
    edges[cnt].next=head[u];
    head[u]=cnt;
}//head[u]代表以u出发的第一条边的编号,edge[head[u]就是这条边
//把i++换成edge[i].next
//for(int i=head[u];i;i=edge[i].next)

int n,m,a,b,t;

void dfs(int rt,int pa,int dis){
    fa[rt]=pa; dist[rt]=dis;
    for(int i=head[rt];i;i=edges[i].next){
        int cur=edges[i].to;
        if(cur==pa) continue;
        dfs(cur,rt,dis+edges[i].val);
    }
}

bool find(int x,int y){
    //寻找x的祖先中有没有y
    if(fa[x]==y) return 1;
    if(fa[x]==1) return 0;
    return find(fa[x],y);
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i"%d%d%d",&a,&b,&t);
        add(a,b,t);
        add(b,a,t);
    }
    dfs(1,1,0);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&k,&q);
        if(find(q,k)){
            ans1++;
            ans2+=(dist[q]-dist[k]);
        }
    }
    printf("%d\n%lld",ans1,ans2);
    return 0;
} 

解法2:用倍增求LCA

#include 
#include 
#include 
#define maxn 100100
using namespace std;

int cnt,ans1=0,head[maxn],k,q;
long long dist[maxn],p[maxn][20],dep[maxn],ans2=0;

struct edge{
    int to,next,val;
}edges[maxn]; 

void add(int u,int v,int val){
    edges[++cnt].to=v;
    edges[cnt].val=val;
    edges[cnt].next=head[u];
    head[u]=cnt;
}//head[u]代表以u出发的第一条边的编号,edge[head[u]就是这条边
//把i++换成edge[i].next
//for(int i=head[u];i;i=edge[i].next)

int n,m,a,b,t;

void dfs(int rt,int father,int dis){
    dist[rt]=dis;
    for(int i=head[rt];i!=0;i=edges[i].next){
        if(!dep[edges[i].to]&&edges[i].to!=father){
            dep[edges[i].to]=dep[rt]+1;
            p[edges[i].to][0]=rt;
            dfs(edges[i].to,rt,dis+edges[i].val);
        }
    }
}

void init_bz(){
    for(int j=1;(1<for(int i=1;i<=n;i++)
            if(p[i][j-1]!=-1)
                p[i][j]=p[p[i][j-1]][j-1];
    return;
}


int LCA(int a,int b){
    int i,j;
    if(dep[a]for(i=0;(1<for(j=i;j>=0;j--)
        if(dep[a]-(1<=dep[b]) a=p[a][j];
    if(a==b) return a;
    for(j=i;j>=0;j--)
        if(p[a][j]!=-1&&p[a][j]!=p[b][j]){
            a=p[a][j];
            b=p[b][j];
        }
    return p[a][0];
}

int main(){
    scanf("%d%d",&n,&m);
    memset(p,-1,sizeof(p));
    memset(dep,0,sizeof(dep));
    memset(head,0,sizeof(head));
    for(int i=1;iscanf("%d%d%d",&a,&b,&t);
        add(a,b,t);
        add(b,a,t);
    }
    dfs(1,1,0); dep[1]=0; p[1][0]=1;
    init_bz(); 
    for(int i=1;i<=m;i++){
        scanf("%d%d",&k,&q);
        int temp=LCA(k,q);
        if(k==temp){
            ans1++; ans2+=dist[q]-dist[k];
        }
    }
    printf("%d\n%lld",ans1,ans2);
    return 0;
} 

你可能感兴趣的:(NOIp_复习,NOIp_数据结构,LCA,RMQ,Tarjan,倍增)