【BZOJ-2588】Count on a tree 主席树 + 倍增
2588: Spoj 10628. Count on a tree
Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 3749 Solved: 873
[Submit][Status][Discuss]
Description
给定一棵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
Solution
区间第k小?必然是主席树
树上第k小?显然把主席树建在树上即可,亦或把主席树建在DFS序上
那么考虑把主席树建在树上,仍旧需要DFS序,还需要预处理出LCA,对建树和询问做一些修改即可
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define maxn 100010 int n,m;int val[maxn];int ls[maxn],num; struct data{int to,next;}edge[maxn<<1]; int head[maxn],cnt; void add(int u,int v){cnt++;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt;} void insert(int u,int v){add(u,v);add(v,u);} int steck[maxn],top,father[maxn][25],deep[maxn],pos[maxn]; void dfs(int x) { steck[++top]=x; pos[x]=top; for (int i=1; i<=20; i++) if (deep[x]>=(1<<i)) father[x][i]=father[father[x][i-1]][i-1]; else break; for (int i=head[x]; i; i=edge[i].next) if (father[x][0]!=edge[i].to) father[edge[i].to][0]=x,deep[edge[i].to]=deep[x]+1,dfs(edge[i].to); } int LCA(int x,int y) { if (deep[x]<deep[y]) swap(x,y); int dd=deep[x]-deep[y]; for (int i=0; i<=20; i++) if (dd&(1<<i)) x=father[x][i]; for (int i=20; i>=0; i--) if (father[x][i]!=father[y][i]) x=father[x][i],y=father[y][i]; if (x==y) return x; else return father[x][0]; } int sum[maxn*20],ll[maxn*20],rr[maxn*20],root[maxn<<2],sz; void update(int l,int r,int &now,int fat,int va) { now=++sz; sum[now]=sum[fat]+1; if (l==r) return; ll[now]=ll[fat],rr[now]=rr[fat]; int mid=(l+r)>>1; if (va<=mid) update(l,mid,ll[now],ll[fat],va); else update(mid+1,r,rr[now],rr[fat],va); } int query(int l,int r,int L,int R,int k) { int lca=LCA(L,R),fa=father[lca][0]; int a=root[pos[L]],b=root[pos[R]],c=root[pos[lca]],d=root[pos[fa]]; while (l<r) { int mid=(l+r)>>1; int summ=sum[ll[a]]+sum[ll[b]]-sum[ll[c]]-sum[ll[d]]; if (summ>=k) r=mid,a=ll[a],b=ll[b],c=ll[c],d=ll[d]; else k-=summ,l=mid+1,a=rr[a],b=rr[b],c=rr[c],d=rr[d]; } return l; } int main() { n=read(),m=read(); for (int i=1; i<=n; i++) val[i]=read(),ls[i]=val[i]; sort(ls+1,ls+n+1); for (int i=1; i<=n; i++) if (ls[i]!=ls[i-1]) ls[++num]=ls[i]; for (int i=1; i<=n; i++) val[i]=lower_bound(ls+1,ls+num+1,val[i])-ls; for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v); dfs(1); for (int tmp,i=1; i<=n; i++) tmp=steck[i],update(1,num,root[i],root[pos[father[tmp][0]]],val[tmp]); int lastans=0,ans=0; for (int u,v,k,i=1; i<=m; i++) { u=read(),v=read(),k=read(); u^=lastans; ans=query(1,num,u,v,k); ans=ls[ans]; printf("%d",ans); if (i!=m) puts(""); lastans=ans; } return 0; }
你丫这题卡最后一行的行末换行...强行PE...好像切题又被蛋蛋发现了...
——It's a lonely path. Don't make it any lonelier than it has to be.