文章标题 SPOJ-COT - Count on a tree(LCA+主席树)

题目 链接

题意:求树上的路径 u->v上第k小的节点

分析:普通的第K大,当前的这颗线段树是在前面一颗线段树的基础上建立的,而树上的第K大,当前的线段树可以在其父节点的线段树建立起来。所以我们查询u->v上的第k大就是rt[u]+rt[v]-rt[lca(u,v)]-rt[fa[lca(u,v)]]的第k大。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;

const int mod=1e9+7;
const int maxn=2e5+10;
int n,q;
int a[maxn],ha[maxn]; 

int sum[maxn*40],ls[maxn*40],rs[maxn*40],rt[maxn*40];
int tot;
int sz;
//主席树 
void build (int &num,int l,int r){
    num=++tot;
    sum[num]=0;
    if (r==l)return;
    int mid=(l+r)/2;
    build (ls[num],l,mid);
    build (rs[num],mid+1,r);
}

void update(int &num,int l,int r,int last,int pos){
    num=++tot;
    ls[num]=ls[last];
    rs[num]=rs[last];
    sum[num]=sum[last]+1;
    if (l==r)return ;
    int mid=(l+r)/2;
    if (pos<=mid)update(ls[num],l,mid,ls[last],pos);
    else update(rs[num],mid+1,r,rs[last],pos);
}

int query(int u,int v,int lca,int lcafa,int l,int r,int k){
    if (l==r)return l;
    int mid=(l+r)/2;
    int cnt=sum[ls[u]]+sum[ls[v]]-sum[ls[lca]]-sum[ls[lcafa]];
    if (cnt>=k)return query(ls[u],ls[v],ls[lca],ls[lcafa],l,mid,k);
    else return query(rs[u],rs[v],rs[lca],rs[lcafa],mid+1,r,k-cnt);
}

//LCA
int dis[maxn];//dis[i]表示节点i到树根的距离 
int dp[maxn*2][20];
int first[maxn];//first[i]表示i这个节点第一次出现的标号 
int dep[maxn*2];//深度 
int point[maxn*2];//point[i]表示标号为i所对应的节点 
int vis[maxn];//标记节点是否被访问过了 
int cnt;//遍历是节点的数目 
int fa[maxn];
vector<int>G[maxn];

void dfs(int u,int father,int idx){
    vis[u]=true;
    point[++cnt]=u;//point表示节点的标号
    first[u]=cnt;//first表示u这个节点第一次出现的位置
    dep[cnt]=idx;//深度
    fa[u]=father;
    update(rt[u],1,sz,rt[father],a[u]);
    for (int i=0;iint v=G[u][i];
        if (!vis[v]){
            dfs(v,u,idx+1);
            point[++cnt]=u;
            dep[cnt]=idx;
        }
    }
}
void RMQ_init(int n){
    for (int i=1;i<=n;i++)dp[i][0]=i;
    for (int k=1;(1<for (int i=1;i+(1<1<=n;i++){
            int a=dp[i][k-1];
            int b=dp[i+(1<<(k-1))][k-1];
            if (dep[a]>dep[b])dp[i][k]=b;
            else dp[i][k]=a;
        }
    }
} 

int RMQ(int l,int r){
    int k=0;
    while ((1<<(k+1))<=r-l+1)k++;
    int a=dp[l][k];
    int b=dp[r-(1<1][k];//保存的是编号
    return dep[a]>dep[b]?b:a;
}

int LCA(int u,int v){//求LCA 
    int x=first[u],y=first[v];
    if (x>y)swap(x,y);
    int ans=RMQ(x,y);
    return point[ans];
}

int main()
{
    while (scanf ("%d%d",&n,&q)!=EOF){
        for (int i=0;i<=n;i++)G[i].clear();
        memset (vis,0,sizeof (vis));
        for (int i=1;i<=n;i++){
            scanf ("%d",&a[i]);
            ha[i]=a[i];
        }
        int u,v,k;
        for (int i=0;i1;i++){
            scanf ("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        sort(ha+1,ha+1+n);
        sz=unique(ha+1,ha+1+n)-(ha+1);
        for (int i=1;i<=n;i++){
            a[i]=lower_bound(ha+1,ha+1+sz,a[i])-ha;
        }
        tot=cnt=0;
        build(rt[0],1,sz);
        dfs(1,0,1);
        RMQ_init(2*n-1);
        while (q--){
            scanf ("%d%d%d",&u,&v,&k);
            int lca=LCA(u,v);
            int ans=query(rt[u],rt[v],rt[lca],rt[fa[lca]],1,sz,k);
            printf ("%d\n",ha[ans]);
        }
    }
    return 0;
}
/*
8 6
105 2 9 3 8 5 7 7
1 2        
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
2 5 2
2 5 3
2 5 4
7 8 2
5 7 1
*/

你可能感兴趣的:(数据结构,模板,LCA,主席树)