Atcoder Beginner Contest 309——D-F讲解

文章介绍了两道与图论和动态规划相关的问题,第一题涉及在特定条件下如何最大化连接两个不连通块的路径长度,第二题是关于家族保险覆盖的动态规划问题,需要找出被至少一张保险覆盖的人数。解决方案分别涉及找到起点到连通块内各点最短路径的最大值和动态规划计算每个节点保险能覆盖的最远世代。
摘要由CSDN通过智能技术生成

前言

由于最近期末考试,所以之前几场都没打,给大家带了不便,非常抱歉。

这个暑假,我将会持续更新,并给大家带了更好理解的题解!希望大家多多支持。

由于, A ∼ C A\sim C AC 题比较简单,所以就不写了,如果大家有不会的,可以私信问我。


D - Add One Edge

1. Description

Problem Statement

We have an undirected graph with ( N 1 + N 2 ) (N_1+N_2) (N1+N2) vertices and M M M edges. For i = 1 , 2 , … , M i=1,2,\ldots,M i=1,2,,M, the i i i-th edge connects vertex a i a_i ai and vertex b i b_i bi.

The following properties are guaranteed:
Vertex u u u and vertex v v v are connected, for all integers u u u and v v v with 1 ≤ u , v ≤ N 1 1 \leq u,v \leq N_1 1u,vN1.
Vertex u u u and vertex v v v are connected, for all integers u u u and v v v with N 1 + 1 ≤ u , v ≤ N 1 + N 2 N_1+1 \leq u,v \leq N_1+N_2 N1+1u,vN1+N2.
Vertex 1 1 1 and vertex ( N 1 + N 2 ) (N_1+N_2) (N1+N2) are disconnected.
Consider performing the following operation exactly once:
choose an integer u u u with 1 ≤ u ≤ N 1 1 \leq u \leq N_1 1uN1 and an integer v v v with N 1 + 1 ≤ v ≤ N 1 + N 2 N_1+1 \leq v \leq N_1+N_2 N1+1vN1+N2, and add an edge connecting vertex u u u and vertex v v v.
We can show that vertex 1 1 1 and vertex ( N 1 + N 2 ) (N_1+N_2) (N1+N2) are always connected in the resulting graph; so let d d d be the minimum length (number of edges) of a path between vertex 1 1 1 and vertex ( N 1 + N 2 ) (N_1+N_2) (N1+N2).
Find the maximum possible d d d resulting from adding an appropriate edge to add.

Definition of "connected" Two vertices $u$ and $v$ of an undirected graph are said to be connected if and only if there is a path between vertex $u$ and vertex $v$. ### Constraints

1 ≤ N 1 , N 2 ≤ 1.5 × 1 0 5 1 \leq N_1,N_2 \leq 1.5 \times 10^5 1N1,N21.5×105
0 ≤ M ≤ 3 × 1 0 5 0 \leq M \leq 3 \times 10^5 0M3×105
1 ≤ a i ≤ b i ≤ N 1 + N 2 1 \leq a_i \leq b_i \leq N_1+N_2 1aibiN1+N2
( a i , b i ) ≠ ( a j , b j ) (a_i,b_i) \neq (a_j,b_j) (ai,bi)=(aj,bj) if i ≠ j i \neq j i=j.
Vertex u u u and vertex v v v are connected for all integers u u u and v v v such that 1 ≤ u , v ≤ N 1 1 \leq u,v \leq N_1 1u,vN1.
Vertex u u u and vertex v v v are connected for all integers u u u and v v v such that N 1 + 1 ≤ u , v ≤ N 1 + N 2 N_1+1 \leq u,v \leq N_1+N_2 N1+1u,vN1+N2.
Vertex 1 1 1 and vertex ( N 1 + N 2 ) (N_1+N_2) (N1+N2) are disconnected.
All input values are integers.

Input

The input is given from Standard Input in the following format:

N 1 N_1 N1 N 2 N_2 N2 M M M
a 1 a_1 a1 b 1 b_1 b1
⋮ \vdots
a M a_M aM b M b_M bM

Output

Print the answer.

Sample Input 1

3 4 6
1 2
2 3
4 5
4 6
1 3
6 7

Sample Output 1

5

If we set u = 2 u=2 u=2 and v = 5 v=5 v=5, the operation yields d = 5 d=5 d=5, which is the maximum possible.

Sample Input 2

7 5 20
10 11
4 5
10 12
1 2
1 5
5 6
2 4
3 5
9 10
2 5
1 4
11 12
9 12
8 9
5 7
3 7
3 6
3 4
8 12
9 11

Sample Output 2

4

2. Solution


根据这个图以及题目大意,可以看出题目中所给的无向图一定是分成了两个连通块,并且起点 1 1 1,终点 N 1 + N 2 N_1+N_2 N1+N2一定不再同一个连通块内部

(1)对于连通块 1 1 1,我们只需计算起点 1 1 1 到连通块内各个顶点的最短距离,取出最长的距离 r e s 1 res_1 res1
(2)对于连通块 2 2 2,我们只需计算终点 N 1 + N 2 N_1+N_2 N1+N2 到连通块内各个顶点的最短距离,取出最长的距离 r e s 2 res_2 res2

最后的答案就是 r e s 1 + r e s 2 + 1 res_1+res_2+1 res1+res2+1,因为我们中间还要加一条边,所以距离还会多 1 1 1


3.Code

#include <iostream>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int N = 3e5 +10;

int n1, n2, m;
int u, v;
vector<int> g[N];
int dist[N], st[N];

void bfs(int start) //模版(不多说了,不会的可以问我)
{
	memset(dist, 0x3f, sizeof dist);
	memset(st, 0, sizeof st);
	queue<int> q;
	q.push(start);
	dist[start] = 0;
	
	while (q.size())
	{
		auto t = q.front();
		q.pop();
		
		if (st[t]) continue;
		st[t] = 1;
		
		for (auto c : g[t])
			q.push(c), dist[c] = min(dist[c], dist[t] + 1);
	}
}

int main()
{
	cin >> n1 >> n2 >> m;
	
	while (m --)
	{
		cin >> u >> v;
		g[u].push_back(v), g[v].push_back(u);
	}
	
	bfs(1); //求连通块1内点1到其他点的最短距离
	int res1 = 0;
	for (int i = 1; i <= n1; i ++)
		res1 = max(res1, dist[i]);//取出到所有点中最短距离中最长的
		
	bfs(n1 + n2);//求连通块2内点N1+N2到其他点的最短距离
	int res2 = 0;
	for (int i = n1 + 1; i <= n1 + n2; i ++)
		res2 = max(res2, dist[i]); //取出到所有点中最短距离中最长的
		
	cout << res1 + res2 + 1 << endl;
	
	return 0;
}

4. Time Complexity: O ( N 1 + N 2 ) O(N_1+N_2) O(N1+N2)


E - Family and Insurance

1.Description

Problem Statement

There is a family consisting of person 1 1 1, person 2 2 2, … \ldots , and person N N N. For i ≥ 2 i\geq 2 i2, person i i i’s parent is person p i p_i pi.
They bought insurance M M M times. For i = 1 , 2 , … , M i=1,2,\ldots,M i=1,2,,M, person x i x_i xi bought the i i i-th insurance, which covers that person and their descendants in the next y i y_i yi generations.
How many people are covered by at least one insurance?

Constraints

2 ≤ N ≤ 3 × 1 0 5 2 \leq N \leq 3 \times 10^5 2N3×105
1 ≤ M ≤ 3 × 1 0 5 1 \leq M \leq 3 \times 10^5 1M3×105
1 ≤ p i ≤ i − 1 1 \leq p_i \leq i-1 1pii1
1 ≤ x i ≤ N 1 \leq x_i \leq N 1xiN
1 ≤ y i ≤ 3 × 1 0 5 1 \leq y_i \leq 3 \times 10^5 1yi3×105
All input values are integers.

Input

The input is given from Standard Input in the following format:

N N N M M M
p 2 p_2 p2 … \ldots p N p_N pN
x 1 x_1 x1 y 1 y_1 y1
⋮ \vdots
x M x_M xM y M y_M yM

Output

Print the answer.

Sample Input 1

7 3
1 2 1 3 3 3
1 1
1 2
4 3

Sample Output 1

4

The 1 1 1-st insurance covers people 1 1 1, 2 2 2, and 4 4 4, because person 1 1 1’s 1 1 1-st generation descendants are people 2 2 2 and 4 4 4.

The 2 2 2-nd insurance covers people 1 1 1, 2 2 2, 3 3 3, and 4 4 4, because person 1 1 1’s 1 1 1-st generation descendants are people 2 2 2 and 4 4 4, and person 1 1 1’s 2 2 2-nd generation descendant is person 3 3 3.

The 3 3 3-rd insurance covers person 4 4 4, because person 4 4 4 has no 1 1 1-st, 2 2 2-nd, or 3 3 3-rd descendants.
Therefore, four people, people 1 1 1, 2 2 2, 3 3 3, and 4 4 4, are covered by at least one insurance.

Sample Input 2

10 10
1 1 3 1 2 3 3 5 7
2 1
5 1
4 3
6 3
2 1
7 3
9 2
1 2
6 2
8 1

Sample Output 2

10

2.Solution

在这里插入图片描述
这是样例中所对应的树。

这道题可以运用动态规划
f i f_i fi 表示 i i i 号节点的保险还能延续到多少代。

那么转移就非常简单了:
f i = max ⁡ ( f i , f p i − 1 ) f_i = \max (f_i, f_{p_i} - 1) fi=max(fi,fpi1) (注: p i p_i pi 即题目中所说的含义)。

之后,就进行对每一个点判断:
如果 f i ≥ 0 f_i\ge0 fi0,那么 r e s + + res++ res++

最后的答案就是 r e s res res。(不太明白的话,可以看看代码~~~)


3.Code

#include <iostream>
#include <cstring>

using namespace std;

const int N = 3e5 + 10;

int n, m;
int f[N], p[N];

int main()
{
	cin >> n >> m;
	
	p[1] = 1;
	for (int i = 2; i <= n; i ++)
		cin >> p[i];
	
	memset(f, -1, sizeof f);
		
	while (m --)
	{
		int x, y;
		
		cin >> x >> y;
		
		f[x] = max(f[x], y); //进行最初的赋值,表示x节点可以延续y代
	}
	
	for (int i = 1; i <= n; i ++)
		f[i] = max(f[i], f[p[i]] - 1);
		
	int res = 0;
	for (int i = 1; i <= n; i ++)
		if (f[i] >= 0)
			res ++;
			
	cout << res << endl;
}

4.Time Complexity: O ( N ) O(N) O(N)


F - Box in Box

1. Description

Problem Statement

There are N N N boxes. The i i i-th box has a shape of a rectangular cuboid whose height, width, and depth are h i , w i h_i,w_i hi,wi, and d i d_i di, respectively.
Determine if there are two boxes such that one’s height, width, and depth are strictly greater than those of the other after rotating them if necessary.

Constraints

2 ≤ N ≤ 2 × 1 0 5 2 \leq N \leq 2 \times 10^5 2N2×105
1 ≤ h i , w i , d i ≤ 1 0 9 1 \leq h_i,w_i,d_i \leq 10^9 1hi,wi,di109
All input values are integers.

Input

The input is given from Standard Input in the following format:

N N N
h 1 h_1 h1 w 1 w_1 w1 d 1 d_1 d1
⋮ \vdots
h N h_N hN w N w_N wN d N d_N dN

Output

Print Yes if there are two boxes such that one’s height, width, and depth are strictly greater than those of the other after rotating them if necessary; print No otherwise.

Sample Input 1

3
19 8 22
10 24 12
15 25 11

Sample Output 1

Yes

If you rotate the 2 2 2-nd box to swap its height and depth, the 3 3 3-rd box will have greater height, depth, and width.

Sample Input 2

3
19 8 22
10 25 12
15 24 11

Sample Output 2

No

Sample Input 3

2
1 1 2
1 2 2

Sample Output 3

No

2. Solution

都放在视频里拉~~~,不懂得话可以再来问我。

ABC 309 F题

线段树没有学的同学可以点这里


3. Code

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2e5 + 10;

int n;
struct Node1
{
	int h, w, d;
	void change() //保证h, w, d升序
	{
		if (h > w) swap(h, w);
		if (w > d) swap(w, d);
		if (h > w) swap(h, w);
	}
	bool operator< (const Node1 &t)const //重载小于号
	{
		if (t.h == h)
			return w > t.w;
		return h < t.h;
	}
}cube[N];
vector<int> discrete;
struct Node2
{
	int l, r;
	int mn;
}seg[8 * N]; //*8的原因是我们后面插入的时候为了离散化的方便,将Wi - 1也做成了节点

int find(int x) //离散化
{
	return lower_bound(discrete.begin(), discrete.end(), x) - discrete.begin() + 1;
}

void pushup(int u)
{
	seg[u].mn = min(seg[u << 1].mn, seg[u << 1 | 1].mn);
}

void build(int u, int l, int r) //建树
{
	if (l == r)
		seg[u] = {l, l, 0x3f3f3f3f};
	else
	{
		seg[u] = {l, r};
		int mid = l + r >> 1;
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
}

void modify(int u, int x, int d) //单点修改
{
	if (seg[u].l == x && seg[u].r == x)
		seg[u].mn = min(seg[u].mn, d);
	else
	{
		int mid = seg[u].l + seg[u].r >> 1;
		if (x <= mid) modify(u << 1, x, d);
		else modify(u << 1 | 1, x, d);
		pushup(u);
	}
}

int query(int u, int l, int r) //区间查询
{
	if (seg[u].l >= l && seg[u].r <= r)
		return seg[u].mn;
		
	int mid = seg[u].l + seg[u].r >> 1, res = 0x3f3f3f3f;
	if (l <= mid) res = query(u << 1, l, r);
	if (r > mid) res = min(res, query(u << 1 | 1, l, r));
	
	return res;	
}

int main()
{
	cin >> n;
	
	for (int i = 1; i <= n; i ++)
		cin >> cube[i].h >> cube[i].w >> cube[i].d, cube[i].change();
		
	sort(cube + 1, cube + 1 + n);
	
	for (int i = 1; i <= n; i ++) discrete.push_back(cube[i].w), discrete.push_back(cube[i].w - 1);
	sort(discrete.begin(), discrete.end());
	discrete.erase(unique(discrete.begin(), discrete.end()), discrete.end());
	
	build(1, 0, discrete.size());
	
	for (int i = 1; i <= n; i ++)
	{
		int ans = query(1, 0, find(cube[i].w - 1));
		
		if (ans < cube[i].d)
		{
			cout << "Yes" << endl;
			return 0;
		}
		
		modify(1, find(cube[i].w), cube[i].d);
	}
	
	cout << "No" << endl;
}
东望苍苍,西望茫茫,叶落花凋泪满裳
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值