Description
-
这是一棵有 n 个柠檬的柠檬树,由 n-1 条枝条连接而成。
-
秋天了,柠檬都成熟了, 牛牛和牛妹准备选一些柠檬送给他们的朋友们。
-
对于每一个朋友,牛妹会选择第 l-r 个柠檬送给朋友。具体的采摘方法是:选取尽可能少的树枝,使得区间内的柠檬两两连通。
-
牛牛负责派送柠檬,但他的朋友太多啦,他实在是忙的上气不接下气,所以他想让您来帮忙。
-
n , q ≤ 2 e 5 n,q\le2e5 n,q≤2e5
Solution
- 类似数星星
- 同样有两种方法,首先先转化为到根节点的链并,然后减去lca的深度。
- 其一即用分治,建出虚树,然后分别对于左边和右边的部分分别用一个并查集做,得出虚树上每一条边在那些时候会出现,然后是一个二维偏序。
- 另一种即LCT。考虑我们对于区间[l,r],树上的每一条边用区间中编号最小的点来覆盖,从小到大加入点,对于一个点 x x x,它的祖先的整个链上,有一段段链被 y y y覆盖,这一段链只有当 y < l ≤ x y<l\le x y<l≤x时才会在 x x x的时候计算,因此我们按照右端点离线询问,然后扫一遍过去用树状数组维护每一个左端点的答案,用LCT的access来完成查询一段段链的这个操作。
- 太久没打LCT了练练手,注意splay的时候判断的是当前点是否是根,但不能直接判是否它的父亲为0.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 200005
using namespace std;
int n,q,i,j,k,ans[maxn],tot;
struct quet{int l,r,i;} que[maxn];
int cmp(quet a,quet b){return a.r<b.r;}
struct Treearray{
int s[maxn];
void add(int x,int d){for(;x<=n;x+=x&-x) s[x]+=d;}
void addv(int l,int r,int d){add(l,d),add(r+1,-d);}
int sum(int x,int S=0){for(;x;x-=x&-x) S+=s[x];return S;}
} T;
int em,e[maxn*2],nx[maxn*2],ls[maxn];
void insert(int x,int y){
em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}
namespace LCA{
#define maxp 20
int dfn[maxn],tot,Idfn[maxn],fa[maxn][maxp],dep[maxn];
void dfs(int x,int p){
fa[x][0]=p,dep[x]=dep[p]+1,dfn[x]=++tot,Idfn[tot]=x;
for(int i=1;i<maxp;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
dfs(e[i],x);
}
int t0[maxn*4],t1[maxn*4];
void maketree(int x,int l,int r){
if (l==r){t0[x]=t1[x]=dfn[l];return;}
int mid=(l+r)>>1;
maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
t0[x]=min(t0[x<<1],t0[x<<1^1]);
t1[x]=max(t1[x<<1],t1[x<<1^1]);
}
int mx,mi;
void find(int x,int l,int r,int L,int R){
if (l>R||r<L) return;
if (L<=l&&r<=R) {
mi=min(mi,t0[x]),mx=max(mx,t1[x]);
return;
}
int mid=(l+r)>>1;
find(x<<1,l,mid,L,R),find(x<<1^1,mid+1,r,L,R);
}
void prepare(){dfs(1,0);maketree(1,1,n);}
int getlca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for(int i=maxp-1;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if (x==y) return x;
for(int i=maxp-1;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int deplca(int l,int r){
mx=0,mi=1e9,find(1,1,n,l,r);
int k=getlca(Idfn[mi],Idfn[mx]);
return dep[k];
}
}
using LCA::prepare;
using LCA::deplca;
struct node{int s[2],fa,sum,v,id;} t[maxn*2];
void dfs(int x,int p){
for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
tot++,t[tot].v=t[tot].sum=1;
t[tot].fa=x,t[e[i]].fa=tot;
dfs(e[i],x);
}
}
int getc(int x){return t[t[x].fa].s[1]==x;}
int nroot(int x){return t[t[x].fa].s[0]==x||t[t[x].fa].s[1]==x;}
void upd(int x){t[x].sum=t[x].v+t[t[x].s[0]].sum+t[t[x].s[1]].sum;}
void rotate(int x){
int y=t[x].fa,c=getc(x); t[x].id=t[y].id,t[y].id=0;
t[y].s[c]=t[x].s[c^1]; if (t[y].s[c]) t[t[y].s[c]].fa=y;
if (nroot(y)) t[t[y].fa].s[getc(y)]=x; t[x].fa=t[y].fa;
t[x].s[c^1]=y,t[y].fa=x;
upd(y),upd(x);
}
void splay(int x){
while (nroot(x)){
int y=t[x].fa;
if (nroot(y)) {
if (getc(y)==getc(x)) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x){
int y=0,k=x;
while (x){
splay(x);
T.addv(t[x].id+1,k,t[x].sum-t[t[x].s[1]].sum);
int tmp=T.sum(5);
if (t[x].s[1]) t[t[x].s[1]].id=t[x].id;
t[x].s[1]=y,upd(x);
y=x,x=t[x].fa;
}
splay(k),t[k].id=k;
}
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d%d",&n,&q),tot=n;
for(i=1;i<n;i++) scanf("%d%d",&j,&k),insert(j,k);
prepare(),dfs(1,0);
for(i=1;i<=q;i++) scanf("%d%d",&que[i].l,&que[i].r),que[i].i=i;
sort(que+1,que+1+q,cmp);
j=1;
for(i=1;i<=n;i++){
access(i);
for(;j<=q&&que[j].r==i;j++)
ans[que[j].i]=T.sum(que[j].l)-deplca(que[j].l,que[j].r)+1;
}
for(i=1;i<=q;i++) printf("%d\n",ans[i]);
}