跟光磊學Java開發-JDBC程式設計實戰
推薦幾篇好的部落格
以及這個題單
普通莫隊
莫隊演算法是一種基於分塊思想的離線演算法
可以用來維護區間的答案和區間的資料結構
首先我們把長度為 \(n\) 的序列劃分為 \(\sqrt{n}\) 個長度為 \(\sqrt{n}\) 的塊並標號
然後把所有的詢問離線下來,按照左端點所在的塊的標號排序,如果標號相同,則按照右端點從小到大排序
詢問的時候維護一個區間最左端的指標和一個區間最右端的指標
每次處理一個新的詢問時,就分別移動左指標和右指標,使它們分別與新的詢問的左右端點重合並記錄答案
最後把存到數組裡的答案輸出即可
一般來說,\(n\) 和 \(m\) 可以看做是相等的,所以塊長取 \(\sqrt{n}\) 最優
對於右端點來說,每一個塊內的右端點是單調的,最多移動 \(n\) 的長度,一共有 \(\sqrt{n}\) 個塊,所以複雜度為 \(n\sqrt{n}\)
對於左端點來說,每次最多移動一個塊長,一共有 \(n\) 個左端點,所以複雜度為 \(n\sqrt{n}\)
再加上排序的 \(nlogn\) ,總的時間複雜度就是 \(n\sqrt{n}\)
但是當 \(n\) 和 \(m\) 不相等的時候,塊長取 \(\frac{n}{\sqrt{m}}\) 是最優的,此時時間複雜度為 \(n\sqrt{m}\)
使用莫隊演算法的前提是沒有強制線上,並且每次移動左指標和右指標改變的貢獻能夠快速計算
莫隊演算法在排序的時候還有一個奇偶性優化
如果左端點所屬的聯通塊標號為奇數,按照右端點從小到大排序
否則按照右端點從大到小排序
這樣排序大約要快 \(30\%\) 左右
程式碼
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=1e6+5; int shuyu[maxn],a[maxn],n,m,k,cnt[maxn],num,blo,ans[maxn]; struct asd{ int jla,jlb,id; }b[maxn]; bool cmp(asd aa,asd bb){ if(shuyu[aa.jla]==shuyu[bb.jla]) return shuyu[aa.jla]&1?aa.jlb<bb.jlb:aa.jlb>bb.jlb; return aa.jla<bb.jla; } void xg(rg int now,rg int jud){ num-=cnt[now]*cnt[now]; cnt[now]+=jud; num+=cnt[now]*cnt[now]; } int main(){ n=read(),m=read(),k=read(); blo=sqrt(n); for(rg int i=1;i<=n;i++){ a[i]=read(); shuyu[i]=(i-1)/blo+1; } for(rg int i=1;i<=m;i++){ b[i].jla=read(),b[i].jlb=read(),b[i].id=i; } std::sort(b+1,b+1+m,cmp); rg int l=1,r=0; for(rg int i=1;i<=m;i++){ while(r<b[i].jlb) xg(a[++r],1); while(l>b[i].jla) xg(a[--l],1); while(r>b[i].jlb) xg(a[r--],-1); while(l<b[i].jla) xg(a[l++],-1); ans[b[i].id]=num; } for(rg int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
二維莫隊
莫隊也可以處理二維平面上的問題
但是排序的方式要改變一下
第一關鍵字:左上角的點的橫座標
第二關鍵字:左上角的點的縱座標
第三關鍵字:右下角的點的橫座標
第四關鍵字:右下角的點的縱座標
複雜度 \(n^2m^{\frac{3}{4}}\)
程式碼
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=205,maxm=1e6+5;
int n,m,q,a[maxn][maxn],shuyu[maxn],blo,ans[maxm],nans,sta[maxm],top,cnt[maxm];
struct asd{
int ax,ay,bx,by,id;
asd(){}
asd(int aa,int bb,int cc,int dd,int ee){
ax=aa,ay=bb,bx=cc,by=dd,id=ee;
}
}b[maxm];
bool cmp(asd aa,asd bb){
if(shuyu[aa.ax]==shuyu[bb.ax]){
if(shuyu[aa.ay]==shuyu[bb.ay]){
if(shuyu[aa.bx]==shuyu[bb.bx]) return shuyu[aa.by]<shuyu[bb.by];
else return shuyu[aa.bx]<shuyu[bb.bx];
} else {
return shuyu[aa.ay]<shuyu[bb.ay];
}
} else {
return shuyu[aa.ax]<shuyu[bb.ax];
}
}
void xgh(int aa,int bb,int hs,int op){
for(rg int i=aa;i<=bb;i++){
nans-=cnt[a[hs][i]]*cnt[a[hs][i]];
cnt[a[hs][i]]+=op;
nans+=cnt[a[hs][i]]*cnt[a[hs][i]];
}
}
void xgl(int aa,int bb,int ls,int op){
for(rg int i=aa;i<=bb;i++){
nans-=cnt[a[i][ls]]*cnt[a[i][ls]];
cnt[a[i][ls]]+=op;
nans+=cnt[a[i][ls]]*cnt[a[i][ls]];
}
}
int main(){
n=read(),m=read();
blo=sqrt(std::max(n,m))/2*3;
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
a[i][j]=read();
sta[++top]=a[i][j];
}
}
std::sort(sta+1,sta+1+top);
rg int haha=std::unique(sta+1,sta+1+top)-sta-1;
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=m;j++){
a[i][j]=std::lower_bound(sta+1,sta+1+haha,a[i][j])-sta;
}
}
q=read();
rg int aa,bb,cc,dd;
for(rg int i=1;i<=q;i++){
aa=read(),bb=read(),cc=read(),dd=read();
if(aa>cc) std::swap(aa,cc);
if(bb>dd) std::swap(bb,dd);
b[i]=asd(aa,bb,cc,dd,i);
}
for(rg int i=1;i<=std::max(n,m);i++){
shuyu[i]=(i-1)/blo+1;
}
std::sort(b+1,b+1+q,cmp);
rg int nx=1,ny=1,mx=1,my=1;
nans=1;
cnt[a[1][1]]=1;
for(rg int i=1;i<=q;i++){
while(mx<b[i].bx){
mx++;
xgh(ny,my,mx,1);
}
while(my<b[i].by){
my++;
xgl(nx,mx,my,1);
}
while(nx>b[i].ax){
nx--;
xgh(ny,my,nx,1);
}
while(ny>b[i].ay){
ny--;
xgl(nx,mx,ny,1);
}
while(mx>b[i].bx){
xgh(ny,my,mx,-1);
mx--;
}
while(my>b[i].by){
xgl(nx,mx,my,-1);
my--;
}
while(nx<b[i].ax){
xgh(ny,my,nx,-1);
nx++;
}
while(ny<b[i].ay){
xgl(nx,mx,ny,-1);
ny++;
}
ans[b[i].id]=nans;
}
for(rg int i=1;i<=q;i++){
printf("%d\n",ans[i]);
}
return 0;
}
帶修莫隊
普通莫隊加上了修改操作
那麼排序的時候也要把修改的時間作為一個關鍵字排序
第一關鍵字:左端點屬於的塊
第二關鍵字:右端點屬於的塊
第三關鍵字:到當前查詢的時候已經進行了多少次修改操作
當塊長為 \(n^{\frac{2}{3}}\) 時,複雜度為 \(n^\frac{5}{3}\)
程式碼
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5,mod=1e6+3;
int n,a[maxn],q,shuyu[maxn],cnt[maxn],mp[maxn],blo,ans[maxn],nans,sta[maxn],tp,cnt1,cnt2,jl[maxn];
struct jie{
int l,r,id,tim;
jie(){}
jie(rg int aa,rg int bb,rg int cc,rg int dd){
l=aa,r=bb,id=cc,tim=dd;
}
}b[maxn];
bool cmp(jie aa,jie bb){
if(shuyu[aa.l]==shuyu[bb.l] && shuyu[aa.r]==shuyu[bb.r]){
return shuyu[aa.r]&1?aa.tim<bb.tim:aa.tim>bb.tim;
} else if(shuyu[aa.l]==shuyu[bb.l]){
return shuyu[aa.l]&1?aa.r<bb.r:aa.r>bb.r;
} else {
return shuyu[aa.l]<shuyu[bb.l];
}
}
void ad(rg int val){
mp[cnt[val]]--;
cnt[val]++;
mp[cnt[val]]++;
}
void sc(rg int val){
mp[cnt[val]]--;
cnt[val]--;
mp[cnt[val]]++;
}
struct asd{
int wz,bef,lat;
asd(){}
asd(rg int aa,rg int bb,rg int cc){
wz=aa,bef=bb,lat=cc;
}
}c[maxn];
int main(){
n=read(),q=read();
blo=pow(n,2.0/3.0);
for(rg int i=1;i<=n;i++){
jl[i]=a[i]=read();
sta[++tp]=a[i];
shuyu[i]=(i-1)/blo+1;
}
rg int aa,bb,cc;
for(rg int i=1;i<=q;i++){
aa=read(),bb=read(),cc=read();
if(aa==1){
cnt1++;
b[cnt1]=jie(bb,cc,cnt1,cnt2);
} else {
cnt2++;
c[cnt2]=asd(bb,jl[bb],cc);
sta[++tp]=cc;
jl[bb]=cc;
}
}
std::sort(sta+1,sta+1+tp);
tp=std::unique(sta+1,sta+1+tp)-sta-1;
for(rg int i=1;i<=n;i++) a[i]=std::lower_bound(sta+1,sta+1+tp,a[i])-sta;
for(rg int i=1;i<=cnt2;i++){
c[i].bef=std::lower_bound(sta+1,sta+1+tp,c[i].bef)-sta;
c[i].lat=std::lower_bound(sta+1,sta+1+tp,c[i].lat)-sta;
}
std::sort(b+1,b+1+cnt1,cmp);
rg int nl=1,nr=0,ntim=0;
for(rg int i=1;i<=cnt1;i++){
while(ntim<b[i].tim){
ntim++;
if(c[ntim].wz>=nl && c[ntim].wz<=nr){
sc(c[ntim].bef);
ad(c[ntim].lat);
}
a[c[ntim].wz]=c[ntim].lat;
}
while(ntim>b[i].tim){
if(c[ntim].wz>=nl && c[ntim].wz<=nr){
ad(c[ntim].bef);
sc(c[ntim].lat);
}
a[c[ntim].wz]=c[ntim].bef;
ntim--;
}
while(nr<b[i].r){
nr++;
ad(a[nr]);
}
while(nl>b[i].l){
nl--;
ad(a[nl]);
}
while(nr>b[i].r){
sc(a[nr]);
nr--;
}
while(nl<b[i].l){
sc(a[nl]);
nl++;
}
nans=1;
while(mp[nans]) nans++;
ans[b[i].id]=nans;
}
for(rg int i=1;i<=cnt1;i++) printf("%d\n",ans[i]);
return 0;
}
回滾莫隊
有些情況下,新增值很好做,但是刪除值不好做
還有些情況下,刪除值很好做,但是新增值不好做
此時普通的莫隊無法做到移動左右指標的時候單次 \(O(1)\) 更新答案
就要用到另一種莫隊:回滾莫隊
回滾莫隊又分為兩種:只加不減的和只減不加的
只加不減
這道題要維護最大值
區間伸長的時候很好說,直接和當前的答案取個 \(max\) 即可
但是區間收縮的時候我們卻無法快速地找到次大值,即使我們記錄了次大值,有可能下一次又把次大值刪除
所以我們要讓莫隊只能加入貢獻,不能刪除貢獻
首先,我們把詢問按照莫隊的順序排序
注意排序時不能使用奇偶性優化,而要按照右端點遞增來排
排完序後,詢問被分成了 \(\sqrt{n}\) 段
我們把每一段拿出來單獨處理
設 \(r[i]\) 為第 \(i\) 段的右端點的下標,\(l[i]\)為第 \(i\) 段的左端點的下標
一開始,我們把左指標設為 \(r[i]+1\)
把右指標設為 \(r[i]\)
因為在每一段內右端點都是單調遞增的,所以右端點一定只有加入的貢獻
對於左端點,為了強制只能加入值
我們要在每次操作使用完成後把它回覆成 \(r[i]+1\) 的狀態 ,同時把之前左端點做的貢獻清除
每一段統計完答案後,我們還要把右端點的貢獻清清除
對於長度小於 \(\sqrt{n}\) 的塊,暴力統計即可
程式碼
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5;
int n,a[maxn],q,shuyu[maxn],cnt[maxn],blo,sta[maxn],tp,lmax[maxn],rmax[maxn],cnt2[maxn];
long long ans[maxn],nans;
struct jie{
int l,r,id;
jie(){}
jie(rg int aa,rg int bb,rg int cc){
l=aa,r=bb,id=cc;
}
}b[maxn];
bool cmp(jie aa,jie bb){
if(shuyu[aa.l]==shuyu[bb.l]){
return aa.r<bb.r;
} else {
return shuyu[aa.l]<shuyu[bb.l];
}
}
void ad(rg int val){
cnt[val]++;
nans=std::max(nans,1LL*sta[val]*cnt[val]);
}
long long solve(rg int l,rg int r){
rg long long mans=0;
for(rg int i=l;i<=r;i++) cnt2[a[i]]++;
for(rg int i=l;i<=r;i++){
mans=std::max(mans,1LL*sta[a[i]]*cnt2[a[i]]);
}
for(rg int i=l;i<=r;i++) cnt2[a[i]]--;
return mans;
}
int main(){
memset(lmax,0x3f,sizeof(lmax));
n=read(),q=read();
blo=sqrt(n);
for(rg int i=1;i<=n;i++){
a[i]=read();
sta[++tp]=a[i];
shuyu[i]=(i-1)/blo+1;
}
std::sort(sta+1,sta+1+tp);
tp=std::unique(sta+1,sta+1+tp)-sta-1;
for(rg int i=1;i<=n;i++) a[i]=std::lower_bound(sta+1,sta+1+tp,a[i])-sta;
for(rg int i=1;i<=n;i++){
rmax[shuyu[i]]=std::max(rmax[shuyu[i]],i);
lmax[shuyu[i]]=std::min(lmax[shuyu[i]],i);
}
for(rg int i=1;i<=q;i++){
b[i].l=read(),b[i].r=read(),b[i].id=i;
}
std::sort(b+1,b+1+q,cmp);
rg int nl=1,nr=0,now=1;
rg long long tmp;
for(rg int i=1;i<=shuyu[n];i++){
memset(cnt,0,sizeof(cnt));
nr=rmax[i];
nans=0;
while(shuyu[b[now].l]==i){
nl=rmax[i]+1;
if(b[now].r-b[now].l<=blo){
ans[b[now].id]=solve(b[now].l,b[now].r);
now++;
continue;
}
while(nr<b[now].r) ad(a[++nr]);
tmp=nans;
while(nl>b[now].l) ad(a[--nl]);
ans[b[now].id]=nans;
nans=tmp;
while(nl<=rmax[i]) cnt[a[nl++]]--;
now++;
}
}
for(rg int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}
只減不加
和只加不減的莫隊同理
只要把右端點改成單調遞減
把一開始的右指標置為 \(n\),左指標置為 \(l[i]\) 即可
程式碼
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=2e5+5;
int n,a[maxn],q,shuyu[maxn],cnt[maxn],blo,lmax[maxn],rmax[maxn],cnt2[maxn],ans[maxn],nans,cnt3[maxn];
struct jie{
int l,r,id;
jie(){}
jie(rg int aa,rg int bb,rg int cc){
l=aa,r=bb,id=cc;
}
}b[maxn];
bool cmp(jie aa,jie bb){
if(shuyu[aa.l]==shuyu[bb.l]){
return aa.r>bb.r;
} else {
return shuyu[aa.l]<shuyu[bb.l];
}
}
void sc(rg int val){
cnt[val]--;
if(cnt[val]==0) nans=std::min(nans,val);
}
int solve(rg int l,rg int r){
rg int mans=0;
for(rg int i=l;i<=r;i++){
cnt2[a[i]]++;
}
while(cnt2[mans]) mans++;
for(rg int i=l;i<=r;i++){
cnt2[a[i]]--;
}
return mans;
}
int main(){
memset(lmax,0x3f,sizeof(lmax));
n=read(),q=read();
blo=sqrt(n);
for(rg int i=1;i<=n;i++){
a[i]=read();
shuyu[i]=(i-1)/blo+1;
}
for(rg int i=1;i<=n;i++){
rmax[shuyu[i]]=std::max(rmax[shuyu[i]],i);
lmax[shuyu[i]]=std::min(lmax[shuyu[i]],i);
}
for(rg int i=1;i<=q;i++){
b[i].l=read(),b[i].r=read(),b[i].id=i;
}
std::sort(b+1,b+1+q,cmp);
rg int nl=1,nr=0,now=1,tmp,cs;
for(rg int i=1;i<=shuyu[n];i++){
nans=0;
nr=n;
for(rg int j=lmax[i];j<=nr;j++) cnt[a[j]]++;
while(cnt[nans]) nans++;
while(shuyu[b[now].l]==i){
if(b[now].r-b[now].l<=blo){
ans[b[now].id]=solve(b[now].l,b[now].r);
now++;
continue;
}
nl=lmax[i];
while(nr>b[now].r){
sc(a[nr]);
nr--;
}
tmp=nans;
for(rg int j=nl;j<b[now].l;j++){
cnt3[a[j]]=cnt[a[j]];
}
cs=nl;
while(nl<b[now].l){
sc(a[nl]);
nl++;
}
for(rg int j=cs;j<b[now].l;j++){
cnt[a[j]]=cnt3[a[j]];
}
ans[b[now].id]=nans;
nans=tmp;
now++;
}
for(rg int j=lmax[i];j<=n;j++) cnt[a[j]]=0;
}
for(rg int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
回滾莫隊的複雜度和普通莫隊是相同的
樹上莫隊
首先我們要把樹變成一個序列
普通的 \(dfn\) 序可以處理子樹上的問題
但是處理路徑問題就要用到尤拉序
尤拉序的求法很簡單,在剛 \(dfs\) 到一個點時加入序列,最後退出時也加入一遍
設 \(eul1[i]\) 表示訪問到 \(i\) 時加入尤拉序的時間,\(eul2[i]\) 表示回溯經過 \(i\) 時加入尤拉序的時間
設 \(eul1[x]<eul1[y]\)
若 \(lca(x,y) = x\),這時 \(x,y\)在一條鏈上,那麼\(eul1[x]\) 到 \(eul1[y]\) 這段區間中,有的點出現了兩次,有的點沒有出現過,這些點都是對答案沒有貢獻的,我們只需要統計出現過 \(1\) 次的點就好
若\(lca(x,y) \neq x\),此時 \(x,y\) 位於不同的子樹內,我們只需要按照上面的方法統計 \(eul2[x]\) 到 \(eul1[y]\) 這段區間內的點,但是我們發現這裡面的點不包括 \(lca\),所以要把 \(lca\) 加上
程式碼
SP10707 COT2 - Count on a tree II
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5;
int h[maxn],tot=1,n,q,blo,a[maxn],sta[maxn],tp2,eul1[maxn],eul2[maxn],dfn[maxn],dfnc;
bool vis[maxn];
struct asd{
int to,nxt;
}b[maxn];
void ad(rg int aa,rg int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
int siz[maxn],dep[maxn],son[maxn],fa[maxn],rk[maxn],tp[maxn],nans,ans[maxn],cnt[maxn];
void dfs1(rg int now,rg int lat){
dep[now]=dep[lat]+1;
eul1[now]=++dfnc;
rk[dfnc]=now;
siz[now]=1;
fa[now]=lat;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
dfs1(u,now);
siz[now]+=siz[u];
if(siz[u]>siz[son[now]]) son[now]=u;
}
eul2[now]=++dfnc;
rk[dfnc]=now;
}
void dfs2(rg int now,rg int top){
tp[now]=top;
if(son[now]) dfs2(son[now],top);
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa[now] || u==son[now]) continue;
dfs2(u,u);
}
}
int getlca(rg int xx,rg int yy){
while(tp[xx]!=tp[yy]){
if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
xx=fa[tp[xx]];
}
if(dep[xx]<dep[yy]) return xx;
return yy;
}
int shuyu[maxn];
struct jie{
int l,r,id,anc;
jie(){}
jie(rg int aa,rg int bb,rg int cc,rg int dd){
l=aa,r=bb,id=cc,anc=dd;
}
}c[maxn];
bool cmp(rg jie aa,rg jie bb){
if(shuyu[aa.l]==shuyu[bb.l]){
return shuyu[aa.l]&1?aa.r<bb.r:aa.r>bb.r;
} else {
return shuyu[aa.l]<shuyu[bb.l];
}
}
void xg(rg int now){
rg int val=vis[now]?-1:1;
if(cnt[a[now]]==0) nans++;
cnt[a[now]]+=val;
if(cnt[a[now]]==0) nans--;
vis[now]^=1;
}
int main(){
memset(h,-1,sizeof(h));
n=read(),q=read();
blo=sqrt(n+n);
for(rg int i=1;i<=n;i++){
a[i]=read();
sta[++tp2]=a[i];
}
for(rg int i=1;i<=n+n;i++){
shuyu[i]=(i-1)/blo+1;
}
std::sort(sta+1,sta+1+tp2);
tp2=std::unique(sta+1,sta+1+tp2)-sta-1;
for(rg int i=1;i<=n;i++) a[i]=std::lower_bound(sta+1,sta+1+tp2,a[i])-sta;
rg int aa,bb,cc;
for(rg int i=1;i<n;i++){
aa=read(),bb=read();
ad(aa,bb);
ad(bb,aa);
}
dfs1(1,0);
dfs2(1,1);
for(rg int i=1;i<=q;i++){
aa=read(),bb=read();
if(eul1[aa]>eul1[bb]) std::swap(aa,bb);
cc=getlca(aa,bb);
if(cc==aa){
c[i]=jie(eul1[aa],eul1[bb],i,0);
} else {
c[i]=jie(eul2[aa],eul1[bb],i,cc);
}
}
std::sort(c+1,c+1+q,cmp);
rg int nl=1,nr=0;
for(rg int i=1;i<=q;i++){
while(nr<c[i].r) xg(rk[++nr]);
while(nl>c[i].l) xg(rk[--nl]);
while(nr>c[i].r) xg(rk[nr--]);
while(nl<c[i].l) xg(rk[nl++]);
if(c[i].anc) xg(c[i].anc);
ans[c[i].id]=nans;
if(c[i].anc) xg(c[i].anc);
}
for(rg int i=1;i<=q;i++){
printf("%d\n",ans[i]);
}
return 0;
}