「題解」洛谷 P8339 [AHOI2022] 鑰匙
阿新 • • 發佈:2022-05-13
暴力:走 \(u\to v\) 路徑時,遇到一個鑰匙,將其壓入對應顏色的棧,遇到一個寶箱,將棧頂的鑰匙和其匹配,彈出這個鑰匙。
特殊性質 A:考慮一個鑰匙 \(x\) 及其對應的寶箱 \(y\),會對所有包含 \(x\to y\) 的路徑(這裡的包含要求和 \(x\to y\) 是一個方向),都會產生 \(1\) 的貢獻。那麼考慮在 \(dfn\times dfn\) 平面上,\((x,y)\) 代表的是 \(x\to y\) 對應的答案,每一個鑰匙及其寶箱所對應的路徑 \(x\to y\),根據 \(x,y\) 是否為祖先關係的不同,包含其的路徑是子樹補 \(\times\) 子樹的矩形,或者子樹 \(\times\)
正解考慮借鑑上面兩個做法的思想。
考慮一次暴力的貪心匹配的過程,一個鑰匙和哪個寶箱匹配,和這個鑰匙之前遇到的鑰匙以及它們的匹配情況是無關的。所以對每個鑰匙考慮,其貪心匹配過程中,往各個方向走,匹配到的寶箱集合是一定的。
暴力找出這個集合的過程就是,以這個鑰匙 \(x\) 為根 dfs,維護一個棧,只存與根顏色相同的鑰匙,然後匹配。直到有寶箱 \(y\) 和根代表的鑰匙匹配成功了,那麼 \(x\to y\) 會對所有包含 \(x\to y\) 的路徑產生一個 \(1\)
由於 dfs 的過程中只和同一顏色有關,所以把同一顏色的拉出來,在虛樹上 dfs 找寶箱即可。
時間複雜度 \(\mathcal{O}(n\log n)\).
#include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<ctime> #define pb emplace_back #define mp std::make_pair #define fi first #define se second #define dbg(x) cerr<<"In Line "<< __LINE__<<" the "<<#x<<" = "<<x<<'\n'; #define dpi(x,y) cerr<<"In Line "<<__LINE__<<" the "<<#x<<" = "<<x<<" ; "<<"the "<<#y<<" = "<<y<<'\n'; using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int>pii; typedef pair<ll,int>pli; typedef pair<ll,ll>pll; typedef vector<int>vi; typedef vector<ll>vll; typedef vector<pii>vpii; template<typename T>T cmax(T &x, T y){return x=x>y?x:y;} template<typename T>T cmin(T &x, T y){return x=x<y?x:y;} #define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) char buf[1<<21],*p1=buf,*p2=buf; template<typename T> T &read(T &r){ r=0;bool w=0;char ch=getchar(); while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar(); while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar(); return r=w?-r:r; } template<typename T1,typename... T2> void read(T1 &x, T2& ...y){ read(x); read(y...); } inline int lowbit(int x){return x&(-x);} const int N=1000010; int n,m,ct[N]; int t[N],c[N]; int ctc[N][3]; int fa[N],dep[N]; vi eg[N]; namespace ac{ int dfn[N],ofn[N],fi[N],ed[N],oft,dft,dep[N],fa[N][21],lg[N],siz[N]; int st[21][N],nowc; int top,stk[N]; vi vec[N],et[N]; void dfs1(int x,int f){ fa[x][0]=f;dep[x]=dep[f]+1;siz[x]=1; dfn[x]=++dft;fi[x]=++oft;ofn[oft]=x; for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1]; for(auto v:eg[x])if(v!=f){ dfs1(v,x); siz[x]+=siz[v]; ed[v]=++oft; ofn[oft]=x; } } int LCA(int x,int y){ x=fi[x];y=fi[y]; int l=min(x,y),r=max(x,y); int k=lg[r-l+1]; return dep[st[k][l]]<dep[st[k][r-(1<<k)+1]] ? st[k][l] : st[k][r-(1<<k)+1]; } void merge(int x,int y){ et[x].pb(y);et[y].pb(x); } int Findsbt(int x,int y){ for(int i=20;~i;i--) if(dep[fa[y][i]]>dep[x]) y=fa[y][i]; return y; } int lct; struct Line{ int l,r,h,v; }li[N*10]; struct Que{ int x,y,i; }q[N]; int ans[N]; int tree[N]; void modify(int x,int v){ for(;x<=n;x+=lowbit(x))tree[x]+=v; } void modify(int l,int r,int v){ modify(l,v); if(r<n)modify(r+1,-v); } int query(int x){ int s=0; for(;x;x-=lowbit(x))s+=tree[x]; return s; } void Push(int l1,int r1,int l2,int r2){ if(l1>r1||l2>r2)return ; // cout << l1 << ' ' << r1 << ' ' << l2 << ' ' << r2 << '\n'; li[++lct]={l2,r2,l1,1}; if(r1<n)li[++lct]={l2,r2,r1+1,-1}; } void dfs2(int x,int f,int h,int aci){ if(x!=aci&&c[x]==nowc){ if(t[x]==1)++h; else{ if(!h){ if(fi[aci]<=fi[x]&&ed[x]<=ed[aci]){ int y=Findsbt(aci,x); int l=dfn[x],r=dfn[x]+siz[x]-1; Push(1,dfn[y]-1,l,r); Push(dfn[y]+siz[y],n,l,r); } else{ if(fi[x]<=fi[aci]&&ed[aci]<=ed[x]){ swap(x,aci); int y=Findsbt(aci,x); int l=dfn[x],r=dfn[x]+siz[x]-1; Push(l,r,1,dfn[y]-1); Push(l,r,dfn[y]+siz[y],n); } else{ Push(dfn[aci],dfn[aci]+siz[aci]-1,dfn[x],dfn[x]+siz[x]-1); } } return ; } --h; } } for(auto v:et[x])if(v!=f){ dfs2(v,x,h,aci); } } void init(){ dfs1(1,0); ed[1]=++oft;ofn[oft]=1; for(int i=1;i<=oft;i++)st[0][i]=ofn[i]; for(int i=2;i<=oft;i++)lg[i]=lg[i>>1]+1; for(int i=1;i<=20;i++) for(int j=1;j+(1<<i)-1<=oft;j++) st[i][j]=dep[st[i-1][j]]<dep[st[i-1][j+(1<<(i-1))]] ? st[i-1][j] : st[i-1][j+(1<<(i-1))]; for(int i=1;i<=m;i++){ int x,y;read(x,y); q[i]={dfn[x],dfn[y],i}; } } void JiTangLaiLe(){ for(int i=1;i<=n;i++)vec[c[i]].pb(i); for(int o=1;o<=n;o++){ nowc=o; stk[top=1]=1; sort(vec[o].begin(),vec[o].end(),[](const int &x,const int &y){return dfn[x]<dfn[y];}); vi po; for(auto x:vec[o]){ if(x==1)continue; int t=LCA(x,stk[top]); while(top>1&&dep[t]<dep[stk[top-1]]){ merge(stk[top-1],stk[top]); po.pb(stk[top]);--top; t=LCA(x,stk[top]); } if(dep[t]<dep[stk[top]]){ merge(t,stk[top]); po.pb(stk[top]);--top; if(t!=stk[top])stk[++top]=t; stk[++top]=x; } else stk[++top]=x; } while(top>1){ merge(stk[top],stk[top-1]); po.pb(stk[top]);--top; } po.pb(1); for(auto x:vec[o]){ if(t[x]==1){ dfs2(x,0,0,x); } } for(auto x:po){ vi().swap(et[x]); } } } void Sao(){ sort(li+1,li+lct+1,[](const Line &x,const Line &y){return x.h<y.h;}); sort(q+1,q+m+1,[](const Que &x,const Que &y){return x.x<y.x;}); int pos=1; for(int i=1;i<=m;i++){ while(pos<=lct&&li[pos].h<=q[i].x){ modify(li[pos].l,li[pos].r,li[pos].v); ++pos; } ans[q[i].i]=query(q[i].y); } } void main(){ init(); JiTangLaiLe(); Sao(); for(int i=1;i<=m;i++)cout << ans[i] << '\n'; } } signed main(){ read(n,m); for(int i=1;i<=n;i++){ read(t[i],c[i]); ctc[c[i]][t[i]]++; } for(int i=1;i<n;i++){ int x,y;read(x,y); eg[x].pb(y);eg[y].pb(x); } ac::main(); #ifdef do_while_true cerr<<'\n'<<"Time:"<<clock()<<" ms"<<'\n'; #endif return 0; }