PQ-Tree 学习笔记

为什么 NOIP 模拟会考到这种东西啊?

PQ-Tree 能解决也恐怕仅能解决如下问题:

对于长度为 \(n\) 的排列 \(p\),在线地给出 \(q\) 个限制,每次给定一个集合 \(S\),要求在 \(p\) 中,\(S\) 中的所有数出现位置构成一个连续段,要求对排列计数。

朴素实现的 PQ-Tree 可以给出一个 \(O(nq)\) 的做法,理论上可以做到 \(O(n+\sum |S|)\)

具体地,我们手玩每次将两个限制 \(S_1,S_2\) 合并,发现形成了三个新的限制 \(S_1/S_2,S_2/S_1,S_1\cap S_2\),而且这三个集合出现位置必须相邻,而且 \(S_1\cap S_2\) 必须在中间。

我们继续玩下去,相当于对于每两个不平凡相交的限制 \(S_1,S_2\)(即 \(S_1\not\subset S_2,S_2\not \subset S_1,S_1\cap S_2\neq \emptyset\))可以去分裂这两个限制,直到限制变成了一个树形结构。我们将原排列的数全部放在叶子节点,对该树求一种 dfs 序就可以得到所有可能的 \(p\)。对于非节点,要么该节点的所有儿子节点都可以在 dfs 序中以任意顺序遍历(称其为 P 点),要么只能顺着或者倒着遍历它的儿子序列(称其为 Q 点)。

如何加入限制并修改整颗树呢?我们将限制中要求的数对应的叶子结点标成黑色。对于非叶子如果子树中有黑有白就是灰色,全黑是黑色,全白是白色。

我们根据黑白灰的颜色分布修改树的形态。分讨有点多,可以看 OI-wiki,这里不讲了。

namespace PQTree{
	void assert(bool x){if(!x){puts("No Solution");exit(0);}}
	const int N=200003;
	vi vec[N];
	bool pq[N];
	int stk[N],tp,cnt;
	int apply(){
		if(tp) return stk[tp--];
		return ++cnt;
	}
	void crush(int x){
		vec[x].clear();
		stk[++tp]=x;
	}
	bool tag[N];
	int col[N];
	void paint(int u){
		if(u<=n){col[u]=tag[u]+1;return;}
		col[u]=0;
		for(int v:vec[u]){paint(v);col[u]|=col[v];}
	}
	int seq[N],rk;
	void split(int u){
		if(col[u]!=3){seq[++rk]=u;return;}
		int c[4]={0,0,0,0};
		for(int v:vec[u]) ++c[col[v]];
		assert(c[3]<=1);
		if(pq[u]){
			int las=0;
			bool fl=1;
			for(int v:vec[u]){
				int c=col[v];
				if(c>1) c^=1;
				if(las>c){fl=0;break;}
				las=c;
			}
			if(fl){
				for(int v:vec[u])
					if(col[v]==3) split(v);
					else seq[++rk]=v;
				return;
			}
			reverse(vec[u].begin(),vec[u].end());
			fl=1;las=0;
			for(int v:vec[u]){
				int c=col[v];
				if(c>1) c^=1;
				if(las>c){fl=0;break;}
				las=c;
			}
			assert(fl);
			for(int v:vec[u])
				if(col[v]==3) split(v);
				else seq[++rk]=v;
		}
		else{
			if(c[1]>1){
				int x=apply();pq[x]=0;
				for(int v:vec[u])
					if(col[v]==1) vec[x].emplace_back(v);
				seq[++rk]=x;
			}
			else if(c[1]){for(int v:vec[u]) if(col[v]==1) seq[++rk]=v;}
			if(c[3]){for(int v:vec[u]) if(col[v]==3) split(v);}
			if(c[2]>1){
				int x=apply();pq[x]=0;
				for(int v:vec[u])
					if(col[v]==2) vec[x].emplace_back(v);
				seq[++rk]=x;
			}
			else if(c[2]){for(int v:vec[u]) if(col[v]==2) seq[++rk]=v;}
		}
		crush(u);
	}
	void solve(int u){
		if(col[u]!=3) return;
		int c[4]={0,0,0,0};
		for(int v:vec[u]) ++c[col[v]];
		assert(c[3]<=2);
		if(!c[2]&&c[3]==1){
			for(int v:vec[u])
				if(col[v]==3) return solve(v);
		}
		if(pq[u]){
			int l=0,r=vec[u].size()-1;
			while(col[vec[u][l]]==1) ++l;
			while(col[vec[u][r]]==1) --r;
			for(int i=l+1;i<r;++i) assert(col[vec[u][i]]==2);
			vi tmp;
			int cc=0;
			for(int v:vec[u]){
				if(col[v]==3){
					split(v);
					if(cc) reverse(seq+1,seq+rk+1);
					for(int i=1;i<=rk;++i) tmp.emplace_back(seq[i]);
					rk=0;
				}
				else tmp.emplace_back(v);
				if(col[v]!=1) cc=1;
			}
			vec[u].swap(tmp);
		}
		else{
			vi tmp;
			for(int v:vec[u])
				if(col[v]==1) tmp.emplace_back(v);
			if(c[3]){
				int x=apply();pq[x]=1;
				for(int v:vec[u])
					if(col[v]==3){
						split(v);
						for(int i=1;i<=rk;++i) vec[x].emplace_back(seq[i]);
						rk=0;break;
					}
				if(c[2]>1){
					int y=apply();pq[y]=0;
					for(int v:vec[u])
						if(col[v]==2) vec[y].emplace_back(v);
					vec[x].emplace_back(y);
				}
				else if(c[2]){for(int v:vec[u]) if(col[v]==2) vec[x].emplace_back(v);}
				if(c[3]>1){
					bool ff=0;
					for(int v:vec[u])
						if(col[v]==3){
							if(ff){
								split(v);
								for(int i=rk;i;--i) vec[x].emplace_back(seq[i]);
								rk=0;break;
							}
							ff=1;
						}
				}
				tmp.emplace_back(x);
			}
			else{
				if(c[2]>1){
					int x=apply();pq[x]=0;
					for(int v:vec[u])
						if(col[v]==2) vec[x].emplace_back(v);
					tmp.emplace_back(x);
				}
				else if(c[2]){for(int v:vec[u]) if(col[v]==2) tmp.emplace_back(v);}
			}
			if(tmp.size()==1lu){
				int x=tmp.back();
				vec[u]=vec[x];
				pq[u]=pq[x];
				crush(x);
			}
			else vec[u].swap(tmp);
		}
	}
	void opt(){
		paint(n+1);solve(n+1);
		for(int i=1;i<=n;++i) tag[i]=0;
	}
	void init(){
		pq[cnt=n+1]=0;
		for(int i=1;i<=n;++i)
			vec[n+1].emplace_back(i);
	}
	void clear(){
		for(int i=1;i<=cnt;++i) vec[i].clear();
		cnt=tp=0;
	}
	/*
	 * int calc(int u){
	 *     if(u<=n) return 1;
	 *     int res;
	 *     if(pq[u]) res=2;
	 *     else res=fac[vec[u].size()];
	 *     for(int v:vec[u]) res=(ll)res*calc(v)%P;
	 *     return res;
	 * }
	 * void show(int u){
	 *     if(u<=n){
	 *         printf("Leaf %d\n",u);
	 *         return;
	 *     }
	 *     printf("%c Node %d: ",pq[u]?'Q':'P',u);
	 *     for(int v:vec[u]) printf("%d ",v);
	 *     putchar('\n');
	 *     for(int v:vec[u]) show(v);
	 * }
	 * void gen(int u){
	 *     if(u<=n){lis[++tt]=u;return;}
	 *     for(int v:vec[u]) gen(v);
	 * }
	 */
}
posted @ 2023-08-22 22:02  yyyyxh  阅读(105)  评论(0编辑  收藏  举报