SPOJ_10628_Count_on_a_Tree_(主席树+Tarjan)

描述


http://www.spoj.com/problems/COT/

给出一棵n个节点的树,树上每一个节点有权值.m次询问,求书上u,v路径中第k小的权值.

 

 

COT - Count on a tree

 

no tags 

 

You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight.

We will ask you to perform the following operation:

  • u v k : ask for the kth minimum weight on the path from node u to node v

 

Input

In the first line there are two integers N and M.(N,M<=100000)

In the second line there are N integers.The ith integer denotes the weight of the ith node.

In the next N-1 lines,each line contains two integers u v,which describes an edge (u,v).

In the next M lines,each line contains three integers u v k,which means an operation asking for the kth minimum weight on the path from node u to node v.

Output

For each operation,print its result.

Example

Input:
8 5
8 5
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 
Output:
2
8
9
105

 

 

 

 

 

 

分析


 

POJ_2104_Kth(主席树)

现在是把原来的问题搬到树上去了.首先我们肯定要求lca,新学了Tarjan的离线算法.

每一个点建立到根节点的主席树,这样最后的结果就是u+v-2*lca,如果lca在所要求的区间内,还要再加上lca.(或者u+v-lca-p[lca]).

自己理解一下吧...挺简单的.

 

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <vector>
 4 using namespace std;
 5 
 6 const int maxn=100000+5;
 7 int n,m,cnt,num;
 8 int a[maxn],id[maxn],b[maxn],head[maxn],p[maxn],f[maxn],root[maxn];
 9 bool vis[maxn];
10 struct edge{
11     int to,next;
12     edge(){}
13     edge(int a,int b):to(a),next(b){}
14 }g[maxn<<1];
15 struct Qry{
16     int u,v,k,lca;
17     Qry(){}
18     Qry(int a,int b,int c,int d):u(a),v(b),k(c),lca(d){}
19 }Q[maxn];
20 struct node{ int l,r,s; }t[maxn*20];
21 struct qry{
22     int v,id;
23     qry(){}
24     qry(int a,int b):v(a),id(b){}
25 };
26 vector <qry> q[maxn];
27 
28 inline int find(int x){ return x==f[x]?x:f[x]=find(f[x]); }
29 void add_edge(int u,int v){
30     g[++cnt]=edge(v,head[u]); head[u]=cnt;
31     g[++cnt]=edge(u,head[v]); head[v]=cnt;
32 }
33 void update(int l,int r,int &pos,int d){
34     t[++num]=t[pos]; pos=num; t[pos].s++; 
35     if(l==r) return;
36     int mid=l+(r-l)/2;
37     if(d<=mid) update(l,mid,t[pos].l,d);
38     else update(mid+1,r,t[pos].r,d);
39 }
40 bool cmp(int x,int y){ return a[x]<a[y]; }
41 void dfs(int u){
42     f[u]=u; root[u]=root[p[u]]; update(1,n,root[u],b[u]);
43     for(int i=head[u];i;i=g[i].next){
44         if(g[i].to!=p[u]){
45             p[g[i].to]=u;
46             dfs(g[i].to);
47             f[g[i].to]=u;
48         }
49     }
50     vis[u]=true;
51     int size=q[u].size();
52     for(int i=0;i<size;i++) if(vis[q[u][i].v]) Q[q[u][i].id].lca=find(q[u][i].v);
53 }
54 void init(){
55     scanf("%d%d",&n,&m);
56     for(int i=1;i<=n;i++) scanf("%d",&a[i]), id[i]=i;
57     sort(id+1,id+n+1,cmp);
58     for(int i=1;i<=n;i++) b[id[i]]=i;
59     for(int i=1;i<n;i++){
60         int u,v;
61         scanf("%d%d",&u,&v);
62         add_edge(u,v);
63     }
64     for(int i=1;i<=m;i++){
65         scanf("%d%d%d",&Q[i].u,&Q[i].v,&Q[i].k);
66         q[Q[i].u].push_back(qry(Q[i].v,i)); q[Q[i].v].push_back(qry(Q[i].u,i));
67     }
68 }
69 int query(int l,int r,int x,int y,int ra,int a,int k){
70     if(l==r) return l;
71     int mid=l+(r-l)/2;
72     int s=t[t[x].l].s+t[t[y].l].s-2*t[t[ra].l].s;
73     if(b[a]>=l&&b[a]<=mid) s++;
74     if(k<=s) return query(l,mid,t[x].l,t[y].l,t[ra].l,a,k);
75     else return query(mid+1,r,t[x].r,t[y].r,t[ra].r,a,k-s);
76 }
77 void solve(){
78     dfs(1);
79     for(int i=1;i<=m;i++){
80         if(Q[i].u==Q[i].v){ printf("%d\n",a[Q[i].u]); continue; }
81         printf("%d\n",a[id[query(1,n,root[Q[i].u],root[Q[i].v],root[Q[i].lca],Q[i].lca,Q[i].k)]]);
82     }
83 }
84 int main(){
85     init();
86     solve();
87     return 0;
88 }
View Code

 

转载于:https://www.cnblogs.com/Sunnie69/p/5511684.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值