聯賽模擬測試8 Dash Speed 線段樹分治
阿新 • • 發佈:2020-10-05
題目描述
分析
- 對於測試點\(1\)、\(2\),直接搜尋即可
- 對於測試點\(3 \sim 6\),樹退化成一條鏈,我們可以將其看成序列上的染色問題,用線段樹維護顏色相同的最長序列
- 對於測試點\(7\)、\(8\),肯定是車的速度越大能經過的道路越少,所以我們用類似並查集的方法從大到小依次維護聯通塊的直徑,這裡要用到一個結論:如果兩個點集\(A\)、\(B\)的直徑分別為\((v_1,v_2)(u_1,u_2)\),那麼\(A \cup B\)的直徑一定出現在這\(C_4^2\)種選擇之中,只要列舉每種情況更新答案就行了。
- 對於全部的測試點,需要用到線段樹分治,我們把一條邊插入到線段樹(下標為車速)上的\(log(n)\)
程式碼
#include<cstdio> #include<iostream> #include<vector> #include<stack> #include<cstring> #include<map> inline int read(){ int x=0,fh=1; 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,maxk=105; int n,m,q[maxn],zx[maxn],son[maxn],siz[maxn],dep[maxn],head[maxn],tot=1; struct asd{ int to,next; }b[maxn]; void ad(int aa,int bb){ b[tot].to=bb; b[tot].next=head[aa]; head[aa]=tot++; } void dfs1(int now,int fa){ siz[now]=1; dep[now]=dep[fa]+1; zx[now]=fa; for(int i=head[now];i!=-1;i=b[i].next){ int u=b[i].to; if(u==fa) continue; dfs1(u,now); siz[now]+=siz[u]; if(son[now]==0 || siz[u]>siz[son[now]]){ son[now]=u; } } } int tp[maxn],dfnc,dfn[maxn],rk[maxn]; void dfs2(int now,int top){ tp[now]=top; dfn[now]=++dfnc; rk[dfnc]=now; if(son[now]) dfs2(son[now],top); for(int i=head[now];i!=-1;i=b[i].next){ int u=b[i].to; if(u==zx[now] || u==son[now]) continue; dfs2(u,u); } } int get_LCA(int u,int v){ while(tp[u]!=tp[v]){ if(dep[tp[u]]<dep[tp[v]]) std::swap(u,v); u=zx[tp[u]]; } if(dep[u]<dep[v]) std::swap(u,v); return v; } int jsjl(int u,int v){ return dep[u]+dep[v]-2*dep[get_LCA(u,v)]; } //log(n)求出兩點之間的距離 struct trr{ int l,r; std::vector<int> g; }tr[maxn]; int x[maxn],y[maxn]; void build(int da,int l,int r){ tr[da].l=l,tr[da].r=r; if(l==r){ tr[da].g.clear(); return; } int mids=(tr[da].l+tr[da].r)>>1; build(da<<1,l,mids); build(da<<1|1,mids+1,r); } void xg(int da,int l,int r,int val){ if(tr[da].l>=l && tr[da].r<=r){ tr[da].g.push_back(val); return; } int mids=(tr[da].l+tr[da].r)>>1; if(l<=mids) xg(da<<1,l,r,val); if(r>mids) xg(da<<1|1,l,r,val); } //將邊下放到線段樹的節點上 struct jl{ int zb,yb,zj; jl(){ zb=yb=zj=0; } }jll[maxn]; int ans[maxn],fa[maxn],d[maxn]; int zhao(int xx){ while(xx!=fa[xx]) xx=fa[xx]; return xx; } bool haha; std::stack<jl> st; std::stack<std::pair<int,int> > stt; jl push_up(jl lc,jl rc){ jl now; now.zj=lc.zj; now.zb=lc.zb; now.yb=lc.yb; if(now.zj<rc.zj){ now.zj=rc.zj; now.zb=rc.zb; now.yb=rc.yb; } int aa=lc.zb,bb=lc.yb,cc=rc.zb,dd=rc.yb; int ee=jsjl(aa,cc); if(now.zj<ee){ now.zj=ee; now.zb=aa,now.yb=cc; } ee=jsjl(aa,dd); if(now.zj<ee){ now.zj=ee; now.zb=aa,now.yb=dd; } ee=jsjl(bb,cc); if(now.zj<ee){ now.zj=ee; now.zb=bb,now.yb=cc; } ee=jsjl(bb,dd); if(now.zj<ee){ now.zj=ee; now.zb=bb,now.yb=dd; } return now; } //合併樹的直徑 void bing(int nx,int ny,int &len){ if(nx==1 && ny==5){ haha=1; } else { haha=0; } if(d[nx]>d[ny]) std::swap(nx,ny); stt.push(std::make_pair(nx,d[nx]==d[ny])); fa[nx]=ny; d[ny]+=(d[nx]==d[ny]); st.push(jll[ny]); jll[ny]=push_up(jll[nx],jll[ny]); if(len<jll[ny].zj) len=jll[ny].zj; } //可撤銷並查集 void dfs(int da,int len){ int now=st.size(); for(int i=0;i<tr[da].g.size();i++){ int wz=tr[da].g[i]; int nx=zhao(x[wz]),ny=zhao(y[wz]); if(nx==ny) continue; bing(nx,ny,len); } if(tr[da].l==tr[da].r){ ans[tr[da].l]=len; while(st.size()>now){ d[fa[stt.top().first]]-=stt.top().second; jll[fa[stt.top().first]]=st.top(); fa[stt.top().first]=stt.top().first; st.pop(); stt.pop(); } return; } dfs(da<<1,len); dfs(da<<1|1,len); while(st.size()>now){ d[fa[stt.top().first]]-=stt.top().second; jll[fa[stt.top().first]]=st.top(); fa[stt.top().first]=stt.top().first; st.pop(); stt.pop(); } } //dfs求出答案 int main(){ memset(head,-1,sizeof(head)); n=read(),m=read(); build(1,1,n); for(int i=1;i<=n;i++){ fa[i]=i; jll[i].zb=jll[i].yb=i; } for(int i=1;i<n;i++){ int aa,bb,cc,dd; aa=read(),bb=read(),cc=read(),dd=read(); x[i]=aa,y[i]=bb; xg(1,cc,dd,i); ad(aa,bb); ad(bb,aa); } dfs1(1,0); dfs2(1,1); dfs(1,0); for(int i=1;i<=m;i++){ q[i]=read(); printf("%d\n",ans[q[i]]); } return 0; }