do_while_true

一言(ヒトコト)

「题解」洛谷 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;
}
posted @ 2022-05-13 07:48  do_while_true  阅读(79)  评论(0编辑  收藏  举报