[bzoj2588][count on a tree] (主席树+lca)
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
Sample Output
2 8 9 105 7
HINT
N,M<=100000
暴力自重。。。
Source
Solution
普通的树上主席树
先上我的TLE树链剖分+主席树
#include<map> #include<vector> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 100010 #define buf 1<<21 using namespace std; char B[buf],*p=B; inline int Rin(){ int x=0,f=1; for(;*p<'0'||*p>'9';p++) if(*p=='-')f=-1; for(;*p>='0'&&*p<='9';p++) x=(x<<1)+(x<<3)+*p-'0'; return x*f; } bool vis[N]; vector<int>d; map<int,int>h; int ind,n,q,ans,val[N],sz[N],rk[N],id[N],mx[N],fa[N],dep[N],top[N]; struct pt{ int v;pt *nt; }*fst[N],mem[N<<1],*e=mem; inline void link(int x,int y){ *++e=(pt){y,fst[x]},fst[x]=e; *++e=(pt){x,fst[y]},fst[y]=e; } void dfs1(int x){ rk[++ind]=x, id[x]=ind, vis[x]=sz[x]=1; for(pt *j=fst[x];j;j=j->nt) if(!vis[j->v]) fa[j->v]=x, dep[j->v]=dep[x]+1, dfs1(j->v),sz[x]+=sz[j->v], mx[x]=sz[mx[x]]<sz[j->v]?j->v:mx[x]; } void dfs2(int x){ vis[x]=0; top[x]=x^mx[fa[x]]?x:top[fa[x]]; if(mx[x]){ dfs2(mx[x]); for(pt *j=fst[x];j;j=j->nt) if(vis[j->v])dfs2(j->v); } } inline int lca(int x,int y){ while(top[x]^top[y]) dep[top[x]]>dep[top[y]]? x=fa[top[x]]: y=fa[top[y]]; return dep[x]<dep[y]?x:y; } struct Seg{ Seg *l,*r;int s; Seg(){} Seg(Seg *_l,Seg *_r,int _s) :l(_l),r(_r),s(_s){} }*rt[N],*nil=new Seg(0x0,0x0,0); Seg *build(Seg *p,int s,int t,int k){ if(!(s^t))return new Seg(rt[0],rt[0],p->s+1); int mid=s+t>>1; return k<=mid? new Seg(build(p->l,s,mid,k),p->r,p->s+1): new Seg(p->l,build(p->r,mid+1,t,k),p->s+1); } int secret(Seg *p1,Seg *p2,Seg *p3,Seg *p4,int s,int t,int k){ while(s<t){ int mid=s+t>>1; int c=p1->l->s+p2->l->s-p3->l->s-p4->l->s; if(k<=c) t=mid, p1=p1->l, p2=p2->l, p3=p3->l, p4=p4->l; else k-=c, s=mid+1, p1=p1->r, p2=p2->r, p3=p3->r, p4=p4->r; } return d[s-1]; } inline int feel(int x,int y,int k){ int t=lca(x,y); return secret(rt[id[x]],rt[id[y]],rt[id[t]],rt[id[fa[t]]],1,d.size(),k); } int main(){ fread(B,1,buf,stdin); n=Rin(),q=Rin(); for(int i=1;i<=n;i++) val[i]=Rin(),d.push_back(val[i]); sort(d.begin(),d.end()); d.erase(unique(d.begin(),d.end()),d.end()); for(int i=0;i<d.size();i++) h[d[i]]=i+1; for(int i=1;i<=n;i++) val[i]=h[val[i]]; for(int i=1;i<n;i++){ int x=Rin(),y=Rin(); link(x,y); } dfs1(1);dfs2(1); rt[0]=nil; rt[0]->l=rt[0]->r=rt[0]; for(int i=1;i<=n;i++) rt[i]=build(rt[id[fa[rk[i]]]],1,d.size(),val[rk[i]]); while(q--){ int x=Rin(),y=Rin(),k=Rin(); printf("%d\n",ans=feel(x^ans,y,k)); } return 0; }
ac倍增+主席树
注意root的深度不能设为0
#include<map> #include<vector> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=100100; inline int Rin(){ int x=0,c=getchar(),f=1; for(;c<48||c>57;c=getchar()) if(!(c^45)) f=-1; for(;c>47&&c<58;c=getchar()) x=(x<<1)+(x<<3)+c-48; return x*f; } bool vis[N]; pair<int,int>v[N]; int n,m,ecnt,ans,fst[N],q[N],a[N*32],dep[N],fa[N][22],bin[22],rt[N],tot,sum[N*32],ls[N*32],rs[N*32]; void build(int &pr,int pl,int s,int t,int k){ pr=++tot; sum[pr]=sum[pl]+1; if(!(s^t))return; ls[pr]=ls[pl]; rs[pr]=rs[pl]; int mid=s+t>>1; if(k<=mid)build(ls[pr],ls[pl],s,mid,k); else build(rs[pr],rs[pl],mid+1,t,k); } struct edge{ int v,nxt; }e[N<<1]; inline void link(int x,int y){ e[++ecnt].v=y; e[ecnt].nxt=fst[x]; fst[x]=ecnt; } void bfs(){ int hd=0,tl=1; vis[1]=1;q[0]=1; while(hd^tl){ int now=q[hd++]; build(rt[now],rt[fa[now][0]],1,n,a[now]); for(int j=1;j<=19;j++) if(bin[j]<=dep[now]) fa[now][j]=fa[fa[now][j-1]][j-1]; else break; for(int j=fst[now];j;j=e[j].nxt) if(!vis[e[j].v]) vis[e[j].v]=1, fa[e[j].v][0]=now, dep[e[j].v]=dep[now]+1, q[tl++]=e[j].v; } } int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int j=19;~j;j--) if(dep[fa[x][j]]>=dep[y]) x=fa[x][j]; if(!(x^y))return x; for(int j=19;~j;j--) if(fa[x][j]^fa[y][j]) x=fa[x][j],y=fa[y][j]; return fa[x][0]; } int query(int p1,int p2,int p3,int p4,int s,int t,int k){ if(!(s^t))return v[t].first; int mid=s+t>>1,c=sum[ls[p1]]+sum[ls[p2]]-sum[ls[p3]]-sum[ls[p4]]; if(k<=c)return query(ls[p1],ls[p2],ls[p3],ls[p4],s,mid,k); return query(rs[p1],rs[p2],rs[p3],rs[p4],mid+1,t,k-c); } int req(int x,int y,int k){ int t=lca(x,y); return query(rt[x],rt[y],rt[t],rt[fa[t][0]],1,n,k); } int main(){ for(int i=0;i<=19;i++) bin[i]=1<<i; n=Rin(),m=Rin(); for(int i=1;i<=n;i++) v[i].first=Rin(),v[i].second=i; sort(v+1,v+1+n); for(int i=1;i<=n;i++) a[v[i].second]=i; for(int i=1;i<n;i++){ int x=Rin(),y=Rin(); link(x,y);link(y,x); } dep[1]=1;bfs(); for(int i=1;i<=m;i++) { int x,y,k; scanf("%d%d%d",&x,&y,&k); printf("%d",ans=req(x^ans,y,k)); if(i!=m)puts(""); } return 0; }
可持久化线段树注意事项
1.对于较大的数据范围,用动态开点线段树时注意直接l+r>>1会爆,可以用l+r>>1=(l>>1)+(r>>1)+(l&r&1)
2.对于lca问题,根节点的深度统一设为1
3.树剖是O(2n)的,可能会超时,用倍增+宽搜可能稍稍快一些
4.我认为指针这种东西最好少用,处理不好可能RE