1. 程式人生 > >BZOJ - 3757 樹上莫隊解決離線路徑問題 & 學習心得

BZOJ - 3757 樹上莫隊解決離線路徑問題 & 學習心得

its lca 翻轉 短路徑 i++ memset FN AR mem

題意:給你一棵樹,求u,v最短路徑的XXX(本題是統計權值種類)

今天課上摸魚學了一種有意思的處理路徑方式(其實是鏈式塊狀樹翻車了看別的),據說實際運行跑的比XX記者還快
大概就是像序列莫隊那樣
首先是對暴力查詢的優化
第一關鍵字是塊(樹上分塊),第二關鍵字是dfs序,這樣保證了離線操作的下界最優
其次是轉移的優化
我把大佬的話再轉述一遍:
\(S(u,v)\):\(u-v\)最短路徑所覆蓋的點集
\(S(u,v)=S(root,u)⊕S(root,v)⊕lca(u,v)\)
\(T(u,v)=S(root,u)⊕S(root,v)\)
每次轉移我們只考慮\(T\)的部分,\(lca\)單獨處理
對於某一次距離為1的轉移,如\(u→u‘\)


\(T(u,u‘)=S(root,u)⊕S(root,u‘)\)
\(T(u‘,v)=S(root,u‘)⊕S(root,v)\)
\(T(u‘,v)=S(root,u)⊕S(root,v)⊕S(root,u)⊕S(root,u‘)=T(u,v)⊕T(u,u‘)\)

得出結論\(T(u‘,v)=T(u,v)⊕T(u,u‘)\)
就是說轉移的時候只需多處理\(u-u‘\)\(v-v‘\)即可(推廣後就是任意距離都可以),記得考慮每次單獨處理的\(lca\)(先後翻轉2遍標記)

那麽再經過%hzwer的帖子學習後得出可能正確的代碼(由於無法交題,沒有驗證正確性)

#include<bits/stdc++.h>
#define rep(i,j,k) for(register int i=j;i<=k;i++) #define rrep(i,j,k) for(register int i=j;i>=k;i--) #define erep(i,u) for(register int i=head[u];~i;i=nxt[i]) #define print(a) printf("%lld",(ll)(a)) #define println(a) printf("%lld\n",(ll)(a)) #define printbk(a) printf("%lld ",(ll)(a)) using namespace
std; const int MAXN = 3e4+11; const int INF = 0x7fffffff; typedef long long ll; ll read(){ ll x=0,f=1;register char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int to[MAXN<<1],nxt[MAXN<<1],head[MAXN],tot; void init(){ memset(head,-1,sizeof head); tot=0; } void add(int u,int v){ to[tot]=v; nxt[tot]=head[u]; head[u]=tot++; } int color[MAXN],belong[MAXN],depth[MAXN],dfn[MAXN]; int stk[MAXN],bit[32],limit,root,cnt,CLOCK,top; int anc[MAXN][20]; bool vis[MAXN]; int ANS,cntNum[MAXN],ans[MAXN]; struct QQQ{ int u,v,a,b,id; bool operator < (const QQQ &rhs) const{ if(belong[u]!=belong[rhs.u]){ return belong[u]<belong[rhs.u]; }else{ return dfn[v]<dfn[rhs.v]; } } }Q[MAXN]; int dfs(int u,int fa,int d){ dfn[u]=++CLOCK; anc[u][0]=fa; depth[u]=d; rep(i,1,16){ if(depth[u]<bit[i]) break; anc[u][i]=anc[anc[u][i-1]][i-1]; } int num=0; erep(i,u){ int v=to[i]; if(v==fa) continue; num+=dfs(v,u,d+1); if(num>=limit){ ++cnt; rep(i,1,num) belong[stk[top--]]=cnt; num=0; } } stk[++top]=u; num++; return num; } int lca(int u,int v){ if(depth[u]<depth[v]) swap(u,v); int d=depth[u]-depth[v]; for(int i=0;bit[i]<=d;i++){ if(d>>i&1) u=anc[u][i]; } for(int i=16;i>=0;i--){ if(anc[u][i]!=anc[v][i]){ u=anc[u][i]; v=anc[v][i]; } } if(u==v) return u; else return anc[u][0]; } void rev(int u){ if(!vis[u]){ vis[u]=1; if(cntNum[color[u]]==0) ANS++; cntNum[color[u]]++; }else{ vis[u]=0; if(cntNum[color[u]]==1) ANS--; cntNum[color[u]]--; } } void viss(int u,int v){ while(u!=v){ if(depth[u]>depth[v]) rev(u),u=anc[u][0]; else rev(v),v=anc[v][0]; } } int main(){ int n,m; bit[0]=1;rep(i,1,30) bit[i]=bit[i-1]<<1; while(cin>>n>>m){ init(); limit=sqrt(n)+1; rep(i,1,n) color[i]=read(); rep(i,1,n){ int u=read(); int v=read(); if(u*v==0) root=u|v; else add(u,v),add(v,u); } top=cnt=CLOCK=0; memset(anc,0,sizeof anc); dfs(root,0,1); if(top){ cnt++; while(top) belong[stk[top--]]=cnt; } rep(i,1,m){ Q[i].u=read(); Q[i].v=read(); Q[i].a=read(); Q[i].b=read(); Q[i].id=i; } sort(Q+1,Q+1+m); ANS=0; int t=lca(Q[1].u,Q[1].v); memset(vis,0,sizeof vis); viss(Q[1].u,Q[1].v); rev(lca(Q[1].u,Q[1].v)); ans[Q[1].id]=ANS; rev(lca(Q[1].u,Q[1].v)); if(cntNum[Q[1].a]&&cntNum[Q[1].b]&&Q[1].a!=Q[1].b){ ans[Q[1].id]--; } rep(i,2,m){ viss(Q[i-1].u,Q[i].u); viss(Q[i-1].v,Q[i].v); rev(lca(Q[i].u,Q[i].v)); ans[Q[i].id]=ANS; if(cntNum[Q[i].a]&&cntNum[Q[i].b]&&Q[i].a!=Q[i].b){ ans[Q[i].id]--; } rev(lca(Q[i].u,Q[i].v)); } rep(i,1,m) println(ans[i]); } return 0; }

BZOJ - 3757 樹上莫隊解決離線路徑問題 & 學習心得