首页 > 其他分享 >PQ-Tree

PQ-Tree

时间:2023-08-22 22:13:51浏览次数:27  
标签:PQ emplace int Tree back ++ vec col

为什么 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\) 必须在中间。

我们继续玩下去,发现限制变成了一个树形结构。我们将原排列的数全部放在叶子节点,对该树求一种 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);
	 * }
	 */
}

标签:PQ,emplace,int,Tree,back,++,vec,col
From: https://www.cnblogs.com/yyyyxh/p/PQ-Tree.html

相关文章

  • 【学习笔记】DSU on Tree
    概述DSUonTree即树上启发式合并,重点不在“合并”,而在利用树链剖分的性质对子树问题进行复杂度正确的分治。算法流程递归处理轻儿子的答案递归处理重儿子的答案重新遍历轻儿子子树,计算当前子树的答案如果当前节点是轻儿子,重新遍历整棵子树,清除答案发现一个节点......
  • [CF1790F] Timofey and Black-White Tree 题解
    [CF1790F]TimofeyandBlack-WhiteTree题解题目描述ZYH有一棵\(n\)个节点的树,最初\(c_0\)号节点是黑色,其余均为白色。给定操作序列\(c_1,c_2,\cdots,c_{n-1}\),第\(i\)次操作表示将\(c_i\)号节点染黑。每次操作后,输出距离最近的两个黑点间的距离。两点\(u,v\)间......
  • CF1762E Tree Sum 题解
    题意对于一棵\(n\)个节点的树\(T\),定义\(\operatorname{good}(T)\)为真当且仅当边权\(w\in\left\{-1,1\right\}\)且对于任意节点\(u\),均有\(\displaystylef(u)=\prod\limits_{\left(u,v\right)\inE}w\left(u,v\right)=-1\)。求\[\sum\limits_{\operat......
  • 6Iux6K+t5pqR5YGH5L2c5paH
    第一篇(ฅ´ω`ฅ)题目:假如你是Kitty,明天你因故没有办法参加好朋友Sara的生日聚会,请你写一封电子邮件向她表示歉意并解释原因。(要求:40个单词)DearSara,Howareyou?I'mwritingtotellyouaboutIcan'tattendyourbirthdayparty.Unfortunately,Ihaveacold,Thedoctor......
  • [AGC005C] Tree Restoring 题解
    比较简单的题。思路我们可以把一棵树抽象成一条极长的链上挂了很多的点。观察这样的树的性质。除去中间的每一个\(dis\)至少有两个点的\(a_i=dis\)。考虑这条链的长度为\(s\)。那么对于中间的点,我们可以分两种情况讨论。\(s\)为偶数那么我们必然要求在中间的权值只......
  • createTreeWalker DOM API All In One
    createTreeWalkerDOMAPIAllInOnedocument.createTreeWalker()createTreeWalker(root)createTreeWalker(root,whatToShow)createTreeWalker(root,whatToShow,filter)https://developer.mozilla.org/en-US/docs/Web/API/Document/createTreeWalkerTheTreeWal......
  • Tree-shaking
    Fontasset"MaterialIcons-Regular.otf"wastree-shaken,reducingitfrom1645184to1384bytes(99.9%reduction).Tree-shakingcanbedisabledbyprovidingthe--no-tree-shake-iconsflagwhenbuildingyourapp.翻译搜索复制......
  • 树链剖分 | 洛谷 P4114 Qtree1
    前言题目链接:洛谷P4114Qtree1前置知识:树链剖分题意给定一棵树,有修改边权和查询两点之间边权最大值两种操作,对于每个查询输出结果。解析已经在前置博客里提到,树链剖分可以将树上的任意一条路径划分成不超过\(O(\logn)\)条连续的链,保证划分出的每条链上的节点DFS序......
  • vue-treeselect 树下拉组件被遮挡问题
    vue-treeselect组件官方中文网站: https://www.vue-treeselect.cn/需求背景:在el-tabs内容中添加此组件出现被遮挡问题通过文档查询解决方法<treeselectv-model="params.wardIds":options="hospitalWardTree"value-consists-of="LEAF_PRIORITY"placeholder=......
  • [ARC117D] Miracle Tree
    题目大意给定一棵\(n\)个节点的树,要求构造出一个点权序列\(E\),满足以下三个条件:所有\(E_i\ge1(1\lei\len)\)。对于任意一组\((i,j)(1≤i<j≤N)\),使\(|E_i-E_j|\geq\operatorname{dist}(i,j)\),\(\operatorname{dist}(i,j)\)即树上\(i\)和\(j\)两点距离。......