SPOJ COT 树上第K大(LCA+主席树)

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

题目:给出一棵树,问两个节点之间路径经过的结点的权值的第k小的数。
为了做这题,先去学了一下DFS+RMQ的LCA求法。

这题和普通的第K大类似。
普通的第K大,是从后往前建立主席树,前一个在后一个的基础上修改。
而树上第K大,依旧是每个结点一棵主席树,是在父节点的基础上修改。
那么可以发现这棵主席树是包括所有祖先结点的(就是深度在当前结点之上的)
查询的时候,是两个结点的值的和,还要减掉lca以上的部分。同时要注意处理lca这个 结点的值。
主要不同便是建树是根据父结点,以前查询的时候注意两个主席树的公共部分。

至于我写的效率略低啊,不加输入外挂会TLE。。。
SPOJ的运行速度,勉强卡过
#include<iostream>  
#include<cstdio>  
#include<map>  
#include<cstring>  
#include<cmath>  
#include<vector>  
#include<algorithm>  
#include<set>  
#include<stack>
#include<string>  
#include<ctime>
#include<queue>  
#include<cassert>
#define inf 1000000005  
#define M 10000005 
#define N 110005
#define maxn 210005  
#define eps 1e-8
#define zero(a) fabs(a)<eps  
#define Min(a,b) ((a)<(b)?(a):(b))  
#define Max(a,b) ((a)>(b)?(a):(b))  
#define pb(a) push_back(a)  
#define mp(a,b) make_pair(a,b)  
#define mem(a,b) memset(a,b,sizeof(a))  
#define LL long long  
#define MOD 1000000007
#define sqr(a) ((a)*(a))  
#define Key_value ch[ch[root][1]][0]  
#define test puts("OK");  
#define pi acos(-1.0)
#define lowbit(x) ((-(x))&(x))
#define HASH1 1331
#define HASH2 10001
#define C   240  
#define vi vector<int>
#define TIME 10  
#pragma comment(linker, "/STACK:1024000000,1024000000")  
using namespace std;
void scanf_(int &num){
    char in;  
    while((in=getchar())>'9'||in<'0'); 
    num=in-'0';   
    while(in=getchar(),in>='0'&&in<='9')   
        num*=10,num+=in-'0';
} 
int n,q,a[N];
vi edge[N];
/*              HASH部分            */
int t[N],m;
void Init_hash(){  
    for(int i=1;i<=n;i++)  
        t[i]=a[i];  
    sort(t+1,t+1+n);  
    m=unique(t+1,t+1+n)-t-1;  
}  
int hash(int x){  
    return lower_bound(t+1,t+1+m,x)-t;  
}  
/*              HASH部分             */
/*              主席树部分           */
int T[M],lson[M],rson[M],c[M],tot=0;
int bulid(int l,int r){
    int root=tot++;
    c[root]=0;
    if(l!=r){
        int mid=(l+r)>>1;
        lson[root]=bulid(l,mid);
        rson[root]=bulid(mid+1,r);
    }
    return root;
}
int update(int root,int pos,int val){
    int newroot=tot++,tmp=newroot;
    c[newroot]=c[root]+val;
    int l=1,r=m;
    while(l<r){
        int mid=(l+r)>>1;
        if(pos<=mid){
            lson[newroot]=tot++;rson[newroot]=rson[root];  
            newroot=lson[newroot];root=lson[root];  
            r=mid;  
        }
        else{
            rson[newroot]=tot++;lson[newroot]=lson[root];  
            newroot=rson[newroot];root=rson[root];  
            l=mid+1;  
        }
        c[newroot]=c[root]+val;
    }
    return tmp;
}
int query(int left_root,int right_root,int LCA,int k){
    int lca_root=T[LCA],pos=hash(a[LCA]);
    int l=1,r=m;
    while(l<r){
        int mid=(l+r)>>1;
        int t=c[lson[left_root]]+c[lson[right_root]]-2*c[lson[lca_root]]+(pos>=l&&pos<=mid);
        if(t>=k){
            left_root=lson[left_root];
            right_root=lson[right_root];
            lca_root=lson[lca_root];
            r=mid;
        }
        else{
            k-=t;
            left_root=rson[left_root];
            right_root=rson[right_root];
            lca_root=rson[lca_root];
            l=mid+1;
        }
    }
    return l;
}
/*              主席树部分           */
/*              LCA部分            */
int depth=0,b[N*2],cnt=0;
int p[N],f[N];
void dfs(int u,int pre){
    int t=++depth;
    b[++cnt]=t;
    f[t]=u;
    p[u]=cnt;
    T[u]=update(T[pre],hash(a[u]),1);
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v==pre) continue;
        dfs(v,u);
        b[++cnt]=t;
    }
}
int dp[N*2][20];
void Init_rmq(int n){
    for(int i=1;i<=n;i++)
        dp[i][0]=b[i];
    int m=floor(log(n*1.0)/log(2.0));  
    for(int j=1;j<=m;j++)
        for(int i=1;i<=n-(1<<j)+1;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);  
}
int rmq(int l,int r){  
    int k=floor(log((r-l+1)*1.0)/log(2.0));  
    return min(dp[l][k],dp[r-(1<<k)+1][k]);  
}  
int lca(int a,int b){
    if(p[a]>p[b]) swap(a,b);
    return f[rmq(p[a],p[b])];
}
/*              LCA部分            */
int main(){
    scanf_(n);scanf_(q);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;
        scanf_(u);scanf_(v);
        edge[u].pb(v);
        edge[v].pb(u);
    }
    Init_hash();
    T[0]=bulid(1,m);
    dfs(1,0);
    Init_rmq(cnt);
    while(q--){
        int u,v,k;
        scanf_(u);scanf_(v);scanf_(k);
        printf("%d\n",t[query(T[u],T[v],lca(u,v),k)]);
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]中的描述,p1168问题使用了线段树解法。在构造树的过程中,需要遍历整棵树,所以时间复杂度为O(n)。但是在改变一个元素的值时,时间复杂度只有O(log(n))。求和的时候,树的节表示一个索引范围内元素值之和,只要将区间分割对应上,平均时间复杂度是O(log(n)),最坏情况下不会超过O(n*log(n))。\[1\] 根据引用\[2\]中的描述,QUERY 5 12应该是第4、5条边的极大值。\[2\] 根据引用\[3\]中的描述,代码中的if(L<=MID)和else if(R>MID)的判断条件是为了确保查询范围在左子树或右子树中。如果加上else,会导致错误。\[3\] #### 引用[.reference_title] - *1* [leetCode307:线段树解法](https://blog.csdn.net/cyd1999/article/details/123963164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spoj 375 Qtree 树链剖分 + 线段树 解法](https://blog.csdn.net/niuox/article/details/8145842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值