Count on a tree II(树上莫队)
Posted hsez-cyx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Count on a tree II(树上莫队)相关的知识,希望对你有一定的参考价值。
Description
题目描述
给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。
输入格式
第一行有两个整数n和m(n=40000,m=100000)。
第二行有n个整数。第i个整数表示第i个节点表示的整数。
在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。
在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。
输出格式
对于每个询问,输出结果。
Solution
树上莫队模板
先按照欧拉序(两次访问的时间,此处不赘述了)对树上的节点分块
当询问为x,y时(设st[x]<st[y])
(i)x为y的祖先
访问区间为st[x]-st[y]
(ii)x不为y的祖先
访问区间为ed[x]-st[y]
解释:
- 因为x,y都在lca的子树上且x比y先被访问,所以访问完x回溯后再访问y,即ed[x]<st[y]
- 先将x-lca上的点访问一遍(ed[x]-ed[lca至x路径上的son]),再将lca至y上的点访问一遍(st[lca至y路径上的son]-st[y])
- 由于st[lca]<ed[x] && st[y]<ed[lca],所以lca没有被算进去,要特判
还有一件特别重要的事:
这题非要用树剖求lca!!!(zz,卡我好久...)
Code
#include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cmath> using namespace std; const int N=1e5+10,M=4e4+10; int ans[N],an,s_d[M],s_u[M],u,v,n,m,d[M],cnt,tot,pos[N],st[M],ed[M],si,bl,fa[M],dep[M],b[N]; int top[M],son[M],siz[M]; vector <int> link[N]; struct node { int l,r,id,lca; bool operator <(const node &o)const { return pos[l]^pos[o.l]?l<o.l:(pos[l]&1?r<o.r:r>o.r); } }q[N]; struct mode { int v,id; bool operator <(const mode &o)const { return v<o.v; } }a[M]; void dfs(int u,int fx) { st[u]=++cnt,b[cnt]=u; fa[u]=fx,dep[u]=dep[fx]+1; int size=link[u].size(); for(int i=0;i<size;i++) { int v=link[u][i]; if(v!=fx) dfs(v,u); } ed[u]=++cnt,b[cnt]=u; } void dfs1(int u) { siz[u]=1; int size=link[u].size(); for(int i=0;i<size;i++) { int v=link[u][i]; if(v==fa[u]) continue; dfs1(v),siz[u]+=siz[v]; if(son[u]==0 || siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u) { if(son[u]) top[son[u]]=top[u],dfs2(son[u]); else return ; int size=link[u].size(); for(int i=0;i<size;i++) { int v=link[u][i]; if(v==fa[u] || v==son[u]) continue; top[v]=v,dfs2(v); } } int Lca(int x,int y) { int px=top[x],py=top[y]; while(px!=py) if(dep[px]>dep[py]) x=fa[px],px=top[x]; else y=fa[py],py=top[y]; if(dep[x]>dep[y]) return y; else return x; } void get(int x,int y) { if(x==0) return; s_u[x]+=y; if(s_u[x]&1) { s_d[d[x]]++; if(s_d[d[x]]==1) an++; } else { s_d[d[x]]--; if(s_d[d[x]]==0) an--; } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i].v),a[i].id=i; sort(a+1,a+1+n); for(int i=1;i<=n;i++) { if(i==1 || a[i].v!=a[i-1].v) cnt++; d[a[i].id]=cnt; } for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); link[u].push_back(v); link[v].push_back(u); } cnt=0,dfs(1,0); dfs1(1),top[1]=1,dfs2(1); for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); if(st[u]>st[v]) swap(u,v); int w=Lca(u,v); if(w==u) q[i].l=st[u],q[i].r=st[v],q[i].lca=0; else q[i].lca=d[w],q[i].l=ed[u],q[i].r=st[v]; q[i].id=i; } n=cnt; si=sqrt(n); bl=(n+si-1)/si; for(int i=1;i<=bl;i++) { int z=min(n,i*si); for(int j=(i-1)*si+1;j<=z;j++) pos[j]=i; } sort(q,q+m); int nl=0,nr=0; for(int i=0;i<m;i++) { while(nl>q[i].l) get(b[--nl],1); while(nl<q[i].l) get(b[nl++],-1); while(nr>q[i].r) get(b[nr--],-1); while(nr<q[i].r) get(b[++nr],1); ans[q[i].id]=an; if(q[i].lca) ans[q[i].id]+=(s_d[q[i].lca]==0); } for(int i=0;i<m;i++) printf("%d ",ans[i]); return 0; }
以上是关于Count on a tree II(树上莫队)的主要内容,如果未能解决你的问题,请参考以下文章
SPOJ.COT2 Count on a tree II(树上莫队)
SP10707 COT2 - Count on a tree II (树上莫队)
SPOJ10707 COT2 - Count on a tree II 树上莫队
SPOJ COT2 - Count on a tree II(树上莫队)