1. 程式人生 > 其它 >「題解」洛谷 P8339 [AHOI2022] 鑰匙

「題解」洛谷 P8339 [AHOI2022] 鑰匙

暴力:走 \(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\)

子樹補的矩形,或者子樹 \(\times\) 子樹的矩形。那麼問題就變成了矩形 \(+1\),單點求值問題,用樹狀陣列掃描線即可。

正解考慮借鑑上面兩個做法的思想。

考慮一次暴力的貪心匹配的過程,一個鑰匙和哪個寶箱匹配,和這個鑰匙之前遇到的鑰匙以及它們的匹配情況是無關的。所以對每個鑰匙考慮,其貪心匹配過程中,往各個方向走,匹配到的寶箱集合是一定的。

暴力找出這個集合的過程就是,以這個鑰匙 \(x\) 為根 dfs,維護一個棧,只存與根顏色相同的鑰匙,然後匹配。直到有寶箱 \(y\) 和根代表的鑰匙匹配成功了,那麼 \(x\to y\) 會對所有包含 \(x\to y\) 的路徑產生一個 \(1\)

的貢獻,這裡用特殊性質 A 的掃描線解決即可。

由於 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;
}