您的位置:首页 > 其它

spoj Count on a tree【主席树+在线LCA】

2016-01-25 23:36 387 查看

10628. Count on a tree


Problem code: COT

 

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
7 


一天只做了这么一个题,好歹做出来了6666,(其实是照着终于改对了,逃,所以以后还得多做数据结构的题,对于debug能力很有提高,毕竟动辄200行

说题意:真是嗷嗷简单易读的题,树上两点间的所有数找最小值,说思路也是嗷嗷简单,就是在树上建主席树嘛。第一次做树套树的题,被代码量惊呆了,我把一个二分while改成递归还180行呢,上午基本上看懂了邝斌代码的整体思路以及几个函数,本公举今天心情好,把代码按着自己之前用的模板敲了一遍,又试着拼接,现在是真懂了主席树的那些函数以及在线LCA的算法。要不说出来混总是要还的,前几天刷RMQ的时候把LCA的遗留问题给忘了,这次的题明显不应该用离线的,参考了这篇博客:点击打开链接,看懂了代码,自己写的也是用的他的模板,真心挺简单的。

言归正传,主席树是求区间第k小的值,放到这个题里面,除了query函数需要改之外,其他直接照搬,用的还是孙大神的模板,结构体左右结点的写法看起来很清晰,坑也就坑在query函数里面了,其实我最开始就怀疑是不是这里的问题,把所有能打印的数组打出来看了,都没问题,才仔细看了query。公共祖先的Rt是不应该每次递归都求的!说不通了啊

LCA没问题,但是建树的函数需要用心想,以后,也就是这两周,我要努力刷树套树,想是一码事,根本写不出啊!

/*********
spoj Count on a tree
2016.1.25
152576	2330ms
C++ (g++ 4.9.2)
*********/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

#define maxn 200020
///=============主席树部分开始
struct Node
{
int ls,rs,cnt;
}tr[maxn*40];
int cur,m,n,q,rt[maxn],b[maxn],sortb[maxn];
void tree_init()
{
cur=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
sortb[i]=b[i];
}
sort(sortb+1,sortb+1+n);
m=1;
for(int i=2;i<=n;i++)
{
if(sortb[i]!=sortb[m]) sortb[++m]=sortb[i];
}
}
int build(int l,int r)
{
int k=cur++;
if(l==r)
{
tr[k].cnt=0;
return k;
}
int mid=(l+r)>>1;
tr[k].ls=build(l,mid);
tr[k].rs=build(mid+1,r);
tr[k].cnt=0;
return k;
}
int update(int o,int l,int r,int pos,int val)
{
int k=cur++;
tr[k]=tr[o];
if(l==pos&&r==pos)
{
tr[k].cnt+=val;
return k;
}
int mid=(l+r)>>1;
if(pos<=mid) tr[k].ls=update(tr[o].ls,l,mid,pos,val);
else tr[k].rs=update(tr[o].rs,mid+1,r,pos,val);
tr[k].cnt=tr[tr[k].ls].cnt+tr[tr[k].rs].cnt;
return k;
}
///==============主席树部分结束
///==============在线LCA开始
int dp[maxn*2][25],num;
bool vis[maxn];
struct Edge
{
int u,v,next;///已知的是点权,不是边权!
}e[maxn<<1];
int tot,head[maxn];
void lca_init()
{
tot=0;
num=0;
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
}
inline void add(int u,int v,int &k)
{
e[k].u=u;e[k].v=v;e[k].next=head[u];head[u]=k++;
e[k].u=v;e[k].v=u;e[k].next=head[v];head[v]=k++;
}
int ver[maxn*2],R[maxn*2],first[maxn];///节点编号、深度、(前两个下标是遍历次序)点位置
//因为是深搜的序列,欧拉序列长度=2*n-1,我居然才知道!
void dfs(int u,int pre,int dep)//不可以用vis数组判环,也可以用pre参数
{
ver[++tot]=u;
first[u]=tot;
R[tot]=dep;
for(int k=head[u];k!=-1;k=e[k].next)
{
int v=e[k].v;
if(v==pre) continue;
///  dir[v]=dir[u]+w;因为没有边权值,所以深度=距离 dir[]数组没有用
dfs(v,u,dep+1);
ver[++tot]=u;
R[tot]=dep;
}
}
void ST(int n)
{
for(int i=1;i<=n;i++) dp[i][0]=i;
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
// if(i + (1 << (j - 1)) > n) continue;
int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
dp[i][j]=R[a]<R[b]?a:b;
}
}
int RMQ(int l,int r)
{
int k=0;
while((1<<(k+1))<=r-l+1) k++;
int a=dp[l][k],b=dp[r-(1<<k)+1][k];
return R[a]<=R[b]?a:b;//?
}
int LCA(int u,int v)
{
int x=first[u],y=first[v];
if(x>y) swap(x,y);
int res=RMQ(x,y);
return ver[res];
}
///=============LCA结束
///=============综合部分:树套树+查询
void dfs_build(int u,int pre)
{
int pos=lower_bound(sortb+1,sortb+1+m,b[u])-sortb;
rt[u]=update(rt[pre],1,m,pos,1);
for(int k=head[u];k!=-1;k=e[k].next)
{
int v=e[k].v;
if(v==pre) continue;
dfs_build(v,u);
}
}
int query(int l,int r,int o,int v,int Lca,int k)
{
/* if(l==r) return l;
int mid=(l+r)>>1;
int
int lca=rt[Lca];

if(k<=tmp) return query(l,mid,tr[o].ls,tr[v].ls,tr[lca].ls,k);
else return query(mid+1,r,tr[o].rs,tr[v].rs,tr[lca].rs,k-tmp);*/
int lca_root=rt[Lca];
int pos=lower_bound(sortb+1,sortb+1+m,b[Lca])-sortb;
while(l<r)
{
int mid=(l+r)>>1;
int tmp=tr[tr[v].ls].cnt+tr[tr[o].ls].cnt-2*tr[tr[lca_root].ls].cnt+(pos>=l&&pos<=mid);
if(tmp>=k)
{
o=tr[o].ls;v=tr[v].ls;lca_root=tr[lca_root].ls;
r=mid;
}
else
{
k-=tmp;
o=tr[o].rs;v=tr[v].rs;lca_root=tr[lca_root].rs;
l=mid+1;
}
}
return l;
}
///============综合部分结束
int main()
{
//freopen("cin.txt","r",stdin);
// freopen("out.txt","w",stdout);
while(scanf("%d%d",&n,&q)==2)
{
tree_init();
lca_init();
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);//居然是忘了读入数据==
add(x,y,num);
}
dfs(1,1,0);///就当是1是根节点 以谁为根节点建树都一样,树枝都可以掰过去
ST(n*2-1);
rt[0]=build(1,m);
dfs_build(1,0);
int u,v,kth;
while(q--)
{
scanf("%d%d%d",&u,&v,&kth);
int tmp=LCA(u,v);
//printf("u=%d v=%d lca=%d   ",u,v,tmp);
printf("%d\n",sortb[query(1,m,rt[u],rt[v],tmp,kth)]);
}
/* for(int i=1;i<=n;i++) printf("i=%d,rt=%d,first=%d\n",i,rt[i],first[i]);
for(int i=1;i<=m;i++) printf("i=%d,sortb=%d\n",i,sortb[i]);
for(int i=1;i<=2*n-1;i++) printf("i=%d,ver=%d,R=%d\n",i,ver[i],R[i]);
for(int j=1;(1<<j)<=2*n-1;j++)
for(int i=1;i+(1<<j)-1<=2*n-1;i++)
{
// if(i + (1 << (j - 1)) > n) continue;
printf("i=%d,j=%d,dp=%d\n",i,j,dp[i][j]);
}
/* puts("7,4");
printf("%d\n",sortb[query(1,m,rt[7],rt[4],LCA(7,4),1)]);
printf("%d\n",sortb[query(1,m,rt[7],rt[4],LCA(7,4),2)]);
printf("%d\n",sortb[query(1,m,rt[7],rt[4],LCA(7,4),3)]);
printf("%d\n",sortb[query(1,m,rt[7],rt[4],LCA(7,4),4)]);
puts("5,8");
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),1)]);
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),2)]);
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),3)]);
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),4)]);
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),5)]);
printf("%d\n",sortb[query(1,m,rt[5],rt[8],LCA(5,8),6)]);
puts("");
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),1)]);
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),2)]);
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),3)]);
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),4)]);
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),5)]);
printf("%d\n",sortb[query(1,m,rt[6],rt[8],LCA(6,8),6)]);
puts("");
puts("2,8");
printf("%d\n",sortb[query(1,m,rt[2],rt[8],LCA(2,8),1)]);
printf("%d\n",sortb[query(1,m,rt[2],rt[8],LCA(2,8),2)]);
printf("%d\n",sortb[query(1,m,rt[2],rt[8],LCA(2,8),3)]);
printf("%d\n",sortb[query(1,m,rt[2],rt[8],LCA(2,8),4)]);
puts("1,8");
printf("%d\n",sortb[query(1,m,rt[1],rt[8],LCA(1,8),1)]);
printf("%d\n",sortb[query(1,m,rt[1],rt[8],LCA(1,8),2)]);
printf("%d\n",sortb[query(1,m,rt[1],rt[8],LCA(1,8),3)]);
puts("");*/
}
return 0;
}


祝愿自己今天晚上做梦不要梦到比赛了T^T
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: