Count on a tree
bzoj 2588: Spoj 10628. Count on a tree
http://www.lydsy.com/JudgeOnline/problem.php?id=2588Description
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
Input
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
Output
M行,表示每个询问的答案。最后一个询问不输出换行符
Sample Input
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
0 5 2
10 5 3
11 5 4
110 8 2
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
0 5 2
10 5 3
11 5 4
110 8 2
Sample Output
2
8
9
105
7
8
9
105
7
HINT
HINT:
N,M<=100000
暴力自重。。。
Source
数据结构:主席树
辅助算法:LCA
以点的dfs序为下标,以点权为区间建立主席树
以前做过的主席树在序列上,所以是以前一个节点的线段树为基准建立的
这里在树上,所以可以考虑以根为基准建立线段树
u,v间增加的点数=cnt[u]+cnt[v]-cnt[LCA(u,v)]-cnt[father[LCA(u,v)]]
#include<cstdio> #include<algorithm> #define N 100001 using namespace std; struct count { private: struct node{int l,r,cnt;}tr[N*20]; struct data {int to,next;}e[N*2]; int n,m,a[N],head[N],edge_cnt; int dep[N],son[N],f[N],bl[N],sz; int root[N],cntt,hash[N],hash_tot; int last; public: inline int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();} return x*f; } inline void add(int u,int v) { e[++edge_cnt].to=v;e[edge_cnt].next=head[u];head[u]=edge_cnt; e[++edge_cnt].to=u;e[edge_cnt].next=head[v];head[v]=edge_cnt; } void init() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(),hash[i]=a[i]; int u,v; for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); add(u,v); } } inline void tree_insert(int pre,int & now,int l,int r,int k) { tr[now=++cntt].cnt=tr[pre].cnt+1; if(l==r) return; int mid=l+r>>1; if(k<=mid) { tr[now].r=tr[pre].r; tree_insert(tr[pre].l,tr[now].l,l,mid,k); } else { tr[now].l=tr[pre].l; tree_insert(tr[pre].r,tr[now].r,mid+1,r,k); } } inline void dfs1(int x,int fa) { son[x]++; tree_insert(root[fa],root[x],1,hash_tot,hash[x]); for(int i=head[x];i;i=e[i].next) { if(e[i].to==fa) continue; f[e[i].to]=x; dep[e[i].to]=dep[x]+1; dfs1(e[i].to,x); son[x]+=son[e[i].to]; } } inline void dfs2(int x,int chain)//给重链编号 { bl[x]=chain; int m=0; for(int i=head[x];i;i=e[i].next) { if(e[i].to==f[x]) continue; if(son[e[i].to]>son[m]) m=e[i].to; } if(!m) return; dfs2(m,chain); for(int i=head[x];i;i=e[i].next) { if(e[i].to==f[x]||e[i].to==m) continue; dfs2(e[i].to,e[i].to); } } inline int getlca(int u,int v)//求lca,本函数+上面2个函数为树链剖分求LCA { while(bl[u]!=bl[v]) { if(dep[bl[u]]<dep[bl[v]]) swap(u,v); u=f[bl[u]]; } if(dep[u]<dep[v]) return u; return v; } void discrete() { sort(a+1,a+n+1); hash_tot=unique(a+1,a+n+1)-(a+1); for(int i=1;i<=n;i++) hash[i]=lower_bound(a+1,a+hash_tot+1,hash[i])-a; } inline int tree_query(int x,int y,int lca,int fa_lca,int l,int r,int k) { if(l==r) return a[l]; int mid=l+r>>1,tmp=tr[tr[x].l].cnt+tr[tr[y].l].cnt-tr[tr[lca].l].cnt-tr[tr[fa_lca].l].cnt; if(k<=tmp) tree_query(tr[x].l,tr[y].l,tr[lca].l,tr[fa_lca].l,l,mid,k); else tree_query(tr[x].r,tr[y].r,tr[lca].r,tr[fa_lca].r,mid+1,r,k-tmp); } void init2() { int u,v,k; for(int i=1;i<=m;i++) { u=read();v=read();k=read(); u^=last; int lca=getlca(u,v); last=tree_query(root[u],root[v],root[lca],root[f[lca]],1,hash_tot,k); if(i!=m )printf("%d\n",last); else printf("%d",last); } } void work() { init(); discrete(); dfs1(1,0); dfs2(1,1); init2(); } }a; int main() { a.work(); }
初做犯了一个很蠢的错误:最后一行不输出换行,楞是没看见
不读完题,第二次了。