题解:luogu P3248 ([HNOI2016] 树)
1. Solution
写一个和其他题解都不一样的神秘赛时思路。
首先我们有一个显而易见的暴力,就是根据每一次操作把树建出来,这棵树的点数是 \(O(nm)\) 的,当 \(n=10^5,m=10^5\) 的时候,显然是无法接受的,我们考虑优化。
我们发现有效点的数量实际上并不多,原树上的 \(n\) 个点姑且都算作有效点,每一次粘贴操作的 \(to\),每一次查询操作的 \(fr\) 和 \(to\),实际上只有 \(4\times 10^5\) 个。
诶,像这种原树节点很多,但是有效点尚可接受的范围的题,我们不难想到可以利用虚树来写。
具体的,我们将所有修改操作和询问操作离线,记录所有有效点,然后执行修改操作,不难算出某一次修改操作对应的编号区间,然后我们把这个编号区间内的有效点全部拿出来建立一棵虚树,然后把这棵虚树连到对应的点上。 由于原树上点的编号很大,所以考虑使用一个 map 将原树上点的编号映射到新树上。
具体建虚树的时候,我们先通过主席树算出这些有效点在模板树上对应的点,然后用模板树上点的编号建立虚树,最后通过主席树把模板树上的点再对应回原树即可。
最后查询的时候,在虚树上查询两点距离,很简单,不过多赘述。
2. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=1e5+5,M=2e6+5;
int n,m,q,tot,cnt_dfn;
int dis[N],siz[N],rt[N],dfn[N],L[N],R[N],a[N<<1];
ll idx[N*3];
map<ll,int>mp;
pii opt[N],query[N];
vector<int>e[N];
struct Segment_tree{
int num;
int cnt[M],ls[M],rs[M];
#define mid (l+r>>1)
int New(){
int p=++num;
cnt[p]=0,ls[p]=0,rs[p]=0;
return p;
}
int copy(int p){
int q=New();
cnt[q]=cnt[p],ls[q]=ls[p],rs[q]=rs[p];
return q;
}
void pushup(int p){
cnt[p]=cnt[ls[p]]+cnt[rs[p]];
}
int build(int l,int r){
int p=New();
if(l==r)return p;
ls[p]=build(l,mid),rs[p]=build(mid+1,r);
return p;
}
int change(int p,int l,int r,int x){
int q=copy(p);
if(l==r){
cnt[q]++;
return q;
}
if(mid>=x)ls[q]=change(ls[p],l,mid,x);
else rs[q]=change(rs[p],mid+1,r,x);
pushup(q);
return q;
}
int querykth(int p,int q,int l,int r,int k){
if(l==r)return l;
if(cnt[ls[q]]-cnt[ls[p]]>=k)return querykth(ls[p],ls[q],l,mid,k);
k-=cnt[ls[q]]-cnt[ls[p]];
return querykth(rs[p],rs[q],mid+1,r,k);
}
int querysum(int p,int q,int l,int r,int L,int R){
if(L<=l&&r<=R)return cnt[q]-cnt[p];
int sum=0;
if(mid>=L)sum+=querysum(ls[p],ls[q],l,mid,L,R);
if(mid<R)sum+=querysum(rs[p],rs[q],mid+1,r,L,R);
return sum;
}
int queryrk(int p,int q,int x){
if(x==1)return 1;
return querysum(p,q,1,n,1,x-1)+1;
}
#undef mid
}Set;
namespace CalLca{
#define Min(x,y) (dep[x]<=dep[y]?x:y)
int cnt_dfn;
int dep[N],dfn[N<<1],L[N];
int st[20][N<<1];
void dfs(int u,int fa){
dfn[++cnt_dfn]=u;
L[u]=cnt_dfn;
for(int v:e[u]){
if(v==fa)continue;
dep[v]=dep[u]+1;
dfs(v,u);
dfn[++cnt_dfn]=u;
}
}
void init(){
dfs(1,0);
for(int i=1;i<=cnt_dfn;i++)
st[0][i]=dfn[i];
for(int j=1;j<20;j++)
for(int i=1;i+(1<<j)-1<=cnt_dfn;i++)
st[j][i]=Min(st[j-1][i],st[j-1][i+(1<<j-1)]);
}
int LCA(int u,int v){
if(u==v)return u;
if(L[u]<L[v])swap(u,v);
int j=__lg(L[u]-L[v]+1);
return Min(st[j][L[v]],st[j][L[u]-(1<<j)+1]);
}
#undef Min
}
namespace Tree{
const int M=7e5+5;
int num;
int fa[M],dep[M],top[M],siz[M],son[M];
ll dis[M];
struct Graph{
struct Edge{
int v,w,nxt;
}e[M];
int cnt_edge;
int head[M];
void AddEdge(int u,int v,int w){
e[++cnt_edge]={v,w,head[u]};
head[u]=cnt_edge;
}
}G;
void dfs(int u){
siz[u]=1;
for(int i=G.head[u];i;i=G.e[i].nxt){
int v=G.e[i].v,w=G.e[i].w;
fa[v]=u;
dis[v]=dis[u]+w;
dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
void redfs(int u){
if(son[u]){
top[son[u]]=top[u];
redfs(son[u]);
}
for(int i=G.head[u];i;i=G.e[i].nxt){
int v=G.e[i].v,w=G.e[i].w;
if(v==son[u])continue;
top[v]=v;
redfs(v);
}
}
void init_dist(){
dfs(1);
top[1]=1;
redfs(1);
}
int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])u=fa[top[u]];
else v=fa[top[v]];
}
return dep[u]<dep[v]?u:v;
}
ll dist(int u,int v){
int lca=LCA(u,v);
return dis[u]+dis[v]-2*dis[lca];
}
}
using CalLca::LCA;
using Tree::dist;
void dfs(int u,int fa){
dfn[++cnt_dfn]=u;
L[u]=cnt_dfn;
siz[u]=1;
for(int v:e[u]){
if(v==fa)continue;
dis[v]=dis[u]+1;
Tree::G.AddEdge(u,v,1);
dfs(v,u);
siz[u]+=siz[v];
}
R[u]=cnt_dfn;
}
bool cmp(int x,int y){
return L[x]<L[y];
}
int build(int n,int x,ll now){
sort(a+1,a+n+1,cmp);
int m=n;
a[++m]=x;
for(int i=2;i<=n;i++)
a[++m]=LCA(a[i],a[i-1]);
sort(a+1,a+m+1,cmp);
int tmp=1;
for(int i=2;i<=m;i++)
if(a[i]!=a[tmp])a[++tmp]=a[i];
m=tmp;
for(int i=1;i<=m;i++){
ll idx=now+Set.queryrk(rt[L[x]-1],rt[R[x]],a[i]);
mp[idx]=++Tree::num;
}
for(int i=2,lca;i<=m;i++){
lca=LCA(a[i],a[i-1]);
ll idxu,idxv;
idxu=now+Set.queryrk(rt[L[x]-1],rt[R[x]],lca);
idxv=now+Set.queryrk(rt[L[x]-1],rt[R[x]],a[i]);
Tree::G.AddEdge(mp[idxu],mp[idxv],dis[a[i]]-dis[lca]);
}
return mp[now+Set.queryrk(rt[L[x]-1],rt[R[x]],a[1])];
}
signed main(){
read(n),read(m),read(q);
for(int i=2,u,v;i<=n;i++){
read(u),read(v);
e[u].push_back(v);
e[v].push_back(u);
}
CalLca::init();
Tree::num=n;
dfs(1,0);
rt[0]=Set.build(1,n);
for(int i=1;i<=n;i++)
rt[i]=Set.change(rt[i-1],1,n,dfn[i]);
for(int i=1;i<=m;i++){
read(opt[i].first),read(opt[i].second);
if(opt[i].second>n)idx[++tot]=opt[i].second;
}
for(int i=1;i<=q;i++){
read(query[i].first),read(query[i].second);
if(query[i].first>n)idx[++tot]=query[i].first;
if(query[i].second>n)idx[++tot]=query[i].second;
}
sort(idx+1,idx+tot+1);
tot=unique(idx+1,idx+tot+1)-idx-1;
for(int i=1;i<=n;i++)
mp[i]=i;
ll now=n;
for(int i=1,j=1,cnt;i<=m;i++){
ll x=opt[i].first,to=opt[i].second;
cnt=0;
while(j<=tot&&idx[j]<=now+siz[x]){
int u=Set.querykth(rt[L[x]-1],rt[R[x]],1,n,idx[j]-now);
a[++cnt]=u;
j++;
}
int tmp=build(cnt,x,now);
Tree::G.yoAddEdge(mp[to],tmp,1);
now+=siz[x];
}
Tree::init_dist();
for(int i=1;i<=q;i++){
ll u=query[i].first,v=query[i].second;
write(dist(mp[u],mp[v])),Nxt;
}
}

浙公网安备 33010602011771号