題解 P5838 【[USACO19DEC]Milk Visits G】
阿新 • • 發佈:2020-09-06
發現是一道比較裸的樹上莫隊,於是就開始剛,然後發現好像是最難的一道題……(本題解用於作者加深演算法理解,也歡迎各位的閱讀)
題意
給你一棵樹,樹有點權,詢問一條路徑上是否有點權為 \(c\) 的點。
題解
我們可以比較明顯地發現詢問是很像莫隊的詢問處理的,可以 \(O(1)\) 去擴充套件 \(l\) 和 \(r\) 。但是這題是樹,所以我們需要引入尤拉序的概念。
尤拉序其實很像 \(dfs\) 序,但是會在出棧的時候多記錄一次,我們可以利用尤拉序來將樹上的路徑轉化為莫隊需要的區間問題。
我們可以先畫一張圖:
其中位於節點右側的是入棧時間,位於節點左側的是出棧時間。
我們不妨以每一個點的入棧時間為編號,尤拉序則為:
\[1~2~3~4~4~6~6~3~9~9~11~11~2~14~15~16~16~15~19~20~20~19~14~1 \]
比如對於 \(9\) ~ \(16\) 這一條路徑,我們可以用時間 \(10\) ~ \(16\) 來表示,其中出現兩次的點我們不進行計算,並且還需要多加上 \(9\) 和 \(16\) 的 \(lca\):\(1\) ,這些可以用異或運算和特判來解決。即路徑 \(x\) ~ \(y\):\(lst_x\) ~ \(fir_y\) 。
同時我們可以發現,如果路徑上的點是為祖先關係,我們需要特殊處理,可以發現是 \(fir_x\) ~ \(fir_y\) 。
因此我們將所有的路徑都轉化為區間之後就可以用莫隊離線實現了,複雜度 \(O(n\sqrt n)\)
以上。
程式碼如下:
#include<bits/stdc++.h> using namespace std; const int N=1e5+5,M=1e5+5; int n,m,type[N]; struct Edge{int nxt,to;}e[N<<1];int head[N]; void add(int u,int v,int i){e[i]=Edge{head[u],v},head[u]=i;} int fir[N],lst[N],dfn[N<<1],cnt_dfn=0; int dep[N],fa[N][25]; void dfs(int u) { dfn[++cnt_dfn]=u,fir[u]=cnt_dfn; for(int i=head[u];i;i=e[i].nxt) { if(e[i].to==fa[u][0]) continue; fa[e[i].to][0]=u; dep[e[i].to]=dep[u]+1; dfs(e[i].to); } dfn[++cnt_dfn]=u,lst[u]=cnt_dfn; } int lca(int u,int v) { if(dep[u]>dep[v]) swap(u,v); for(int i=20;i>=0;--i) { if(dep[fa[v][i]]>=dep[u]) v=fa[v][i]; } if(u==v) return u; for(int i=20;i>=0;--i) { if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; } return fa[u][0]; } struct Query{int l,r,lca,c,id;}q[M]; int bel[N<<1],size; bool cmp(Query a,Query b) { if(bel[a.l]^bel[b.l]) return bel[a.l]<bel[b.l]; if(bel[a.l]^1) return a.r<b.r; return a.r>b.r; } int l=1,r=0,tag[N],cnt[N],ans[M]; int main() { cin>>n>>m; for(int i=1;i<=n;++i) scanf("%d",&type[i]); for(int i=1,u,v;i<n;++i) { scanf("%d%d",&u,&v); add(u,v,i<<1); add(v,u,i<<1|1); } dep[1]=1,dfs(1); for(int i=1;i<=20;++i) { for(int j=1;j<=n;++j) fa[j][i]=fa[fa[j][i-1]][i-1]; } size=sqrt(n*2); for(int i=1,cnt=0;i<=n*2;i+=size) { ++cnt; for(int j=i;j<min(i+size,n*2+1);++j) bel[j]=cnt; } for(int i=1;i<=m;++i) { scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].c),q[i].id=i; if(fir[q[i].l]>fir[q[i].r]) swap(q[i].l,q[i].r); q[i].lca=lca(q[i].l,q[i].r); if(q[i].lca==q[i].l) q[i].l=fir[q[i].l]; else q[i].l=lst[q[i].l]; q[i].r=fir[q[i].r]; } // for(int i=1;i<=n*2;++i) // printf("%d ",dfn[i]); // printf("\n"); sort(q+1,q+1+m,cmp); // for(int i=1;i<=m;++i) // printf("%d %d %d\n",q[i].l,q[i].r,q[i].lca); for(int i=1;i<=m;++i) { while(q[i].r>r) { tag[dfn[++r]]^=1; if(tag[dfn[r]]) cnt[type[dfn[r]]]++; else cnt[type[dfn[r]]]--; } while(q[i].r<r) { tag[dfn[r]]^=1; if(tag[dfn[r]]) cnt[type[dfn[r--]]]++; else cnt[type[dfn[r--]]]--; } while(q[i].l>l) { tag[dfn[l]]^=1; if(tag[dfn[l]]) cnt[type[dfn[l++]]]++; else cnt[type[dfn[l++]]]--; } while(q[i].l<l) { tag[dfn[--l]]^=1; if(tag[dfn[l]]) cnt[type[dfn[l]]]++; else cnt[type[dfn[l]]]--; } // printf("$$$%d %d\n",l,r); // for(multiset<int>::iterator i=st.begin();i!=st.end();++i) // printf("%d ",*i); // printf("\n"); ans[q[i].id]=(cnt[q[i].c]||type[q[i].lca]==q[i].c); } for(int i=1;i<=m;++i) printf("%d",ans[i]); printf("\n"); return 0; }