ABC223-EFG Editorial

ABC223(A~H)
打的是 virtual participation,基本上是第一次打 at,因此前面几题没什么经验虽然无比简单但是做得不够快,另外主要是卡在了 E 题上,100min 的时间实在紧!以下 EFG,F 题是赛后快速做出来的,而 E 题和 G 题是直接看题解的。以下为本场比赛有价值的题目整理(H题看难度就是尚且驾驭不了的):

E

问是否可能在左下顶点 \((0,0)\),右上顶点 \((X,Y)\) 的矩形中放置两两没有重合面积的矩形 \(\text{A,B,C}\),使得 \(S(\text{A})\ge A,S(\text{B})\ge B,S(\text{C})\ge C\)

先考虑 2 个矩形的情况。通过思考不难发现,一定存在一条平行于 \(x\) 轴/ \(y\) 轴的直线 \(l\) 将大矩形分成两个部分,使得两个矩形分别完整地存在于两个部分中。
image

进一步考虑 3 个矩形的情况。类似地,一定可以使大矩形划分成下列 4 种类型中的一种,使得每个矩形完整且独立地分布在其中之一。
image
针对每种情况 check 以下是否可行即可。注意 A,B,C 也需要考虑排列(具体见代码)。
https://atcoder.jp/contests/abc223/submissions/27327597

F

给你初始的括号序列 \(S\),并进行 \(q\) 次询问

  • 1 l r:交换 \(S_l,S_r\)
  • 2 l r:输出 \(S_{l...r}\) 是不是合法括号序列

引用某博客一句话:遇到这类(关于括号序列和修改查询的)问题,有套路( 看成 1,) 看成 -1,用数据结构维护
那么有了套路这题就可以轻松 AC 了。
由于 \(S_{l...r}\) 是合法括号序列的充要条件为 \(\begin{equation} \left\{ \begin{aligned} \forall l\le x\le r,\sum_{i=l}^x a_i\ge 0\\ \sum_{i=l}^ra_i=0\\ \end{aligned} \right. \end{equation}\),相当于要维护{这个序列的前缀和}的区间最小值和单点查询,考虑使用线段树,并且为了简化代码将单点查询视作对一个长度为 1 的区间的最小值查询。那么修改操作也很容易了,见代码,区间加值即可。
https://atcoder.jp/contests/abc223/submissions/27326777

G

已知树 \(T\),问有多少个节点 \(u\) 使得从 \(T\) 中删去 \(u\) 和所有与 \(u\) 直接相连的边后,图的最大匹配与 \(T\) 的最大匹配大小相等。

什么是图的最大匹配?

设图的一个边集 \(E'\)\(E'\) 中的所有边两两没有公共端点,\(|E'|_\max\) 即为最大匹配的大小。

根据官方题解,树的最大匹配的大小即为用下述方法染色后黑点的个数。

  • 初始时所有节点为白
  • 自下而上地考虑 \(T\),对于每一个节点如果它的儿子中存在一个白色节点它就染成黑色,否则染成白色

于是,如果根节点为白色,则删去它不会影响,就是一个符合要求的 \(u\)

利用树形 DP 跑一轮 \(O(n)\) 对树染色,再 dfs 一轮换根 DP 求出每个点作为根时是什么颜色,题目解决。

考虑如何换根 DP。
设第一轮的染色后每个节点的颜色为 \(f_i\)(下面成为“原来的颜色”),同时也用更新 \(f_i\) 的方式求出节点 \(i\) 换作根的颜色。换根之后,\(i\) 的儿子不变色,\(i\) 的父亲 \(p\) 成为 \(i\) 的儿子,那么需要求出 \(p\)\(i\) 作为根时的颜色,从而求出 \(i\) 的颜色。考虑怎么求出 \(p\) 的颜色。相当于节点 \(p\) 此时是被边 \((p,i)\) “吊”起来的,注意到 \(p\) 的除 \(i\) 以外儿子不变色而需要得到 \(p\) 的父节点吊起来的颜色,那么我们可以每次求出一个点吊起来的颜色并递归地传给儿子。不好说。这一部分的实现看代码注释。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,ans,f[N],fa[N];
vector<int>G[N];
void dfs(int x,int p){ // 第一轮 DP 对树染色
	fa[x]=p;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(y^p){
			dfs(y,x);
			f[x]|=!f[y]; //儿子一旦是白,x就染黑
		}
	}
}
void dfs2(int x,int p,int val){ // val为父节点p被边(x,p)吊起来的颜色
	if(x!=1)f[x]|=!val; // 父节点此时也成了x的儿子,用来更新x作为根的颜色
	vector<int>pv,pp;pv.clear(),pp.clear();pv.push_back(0),pp.push_back(0); 
	// 根据第一轮DP需要求出儿子按位或和的经验,
	// 记录x的儿子的前缀按位或和pv[son]和后缀按位或和pp[son],
	// 则去除(x,son)后的儿子异或和为pv[son-1]|pv[son+1]
	int siz=G[x].size(),las=0,rl=0;
	for(int i=0;i<siz;i++)if(G[x][i]^p)pv.push_back(las|=!f[G[x][i]]),rl++;
	las=0;
	for(int i=siz-1;i>=0;i--)if(G[x][i]^p)pp.push_back(las|=!f[G[x][i]]);
	pv.push_back(0),pp.push_back(0);
	int lr=0;
	for(int i=1;i<=siz;i++){
		int y=G[x][i-1];
		if(y^p)lr++,dfs2(y,x,(!val)|pv[lr-1]|pp[rl-lr]);
	}
}
int main(){
	cin>>n;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0),dfs2(1,0,1);
	for(int i=1;i<=n;i++)ans+=!f[i];
	cout<<ans;
}
posted @ 2021-11-19 20:17  pengyule  阅读(142)  评论(0)    收藏  举报