ABC 448 A - D 题解

ABC 448 A - D 题解

A:chmin

题意

给你一个长度为 \(N\) 的数组 \(A\),与一个整数 \(X\)。对于 \(i = 1, 2, \cdots , N\),执行以下操作:

  • \(A_i < X\),则将 \(X\) 赋值为 \(A_i\),并输出 1
  • 否则输出 0

思路

遍历整个数组,按照思路进行操作即可。

代码部分

#include <bits/stdc++.h>
using namespace std;
int n, x;
int main()
{
	cin >> n >> x;
	for(int i = 1, a;i <= n;i ++)
	{
		cin >> a;
		if(a < x)
			cout << 1 << endl, x = a;
		else
			cout << 0 << endl;
	}
	return 0;
}

B:Pepper Addiction

题意

这个题意太乱了,我尽量按照它的原意说明白。

\(M\) 种胡椒粉末,第 \(i\) 种有 \(C_i\) 克。
又有 \(N\) 盘菜,第 \(i\) 盘菜只能加第 \(A_i\) 种胡椒粉末,并且最多加入 \(C_i\) 克。
求问我们总共最多能加入几克的胡椒粉末。

思路

这题很容易想到一点:就是不同的菜加同样数量的同种的胡椒粉末达成的效果是一样的
所以其实就是你不用管每道菜加了多少胡椒粉末,尽可能把每道菜加满就可以了。
然后再看最后能用掉总共多少胡椒粉末就可以了。

代码部分

#include <bits/stdc++.h>
using namespace std;
int n, m;
int c[1010], a[1010], b[1010];
long long ans = 0;
int main()
{
	cin >> n >> m;
	for(int i = 1;i <= m;i ++)
		cin >> c[i];
	
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i] >> b[i];
		if(c[a[i]] >= b[i])//如果这道菜能加满胡椒粉末
		{
			c[a[i]] -= b[i];
			ans += b[i];
		}
		else //否则就能加多少加多少
			ans += c[a[i]], c[a[i]] = 0;
	}
	
	cout << ans << endl;
	return 0;
}

C:Except and Min

题意

一个袋子里有 \(N\) 个球,编号为 \(1\)\(N\)。第 \(i\) 个球上写着数字 \(A_i\)
我们需要处理 \(Q\) 项查询,每次查询给出一个长度为 \(K\) 的序列 \(B\)。然后,执行以下操作:

  • 拿走编号为 \(B_1, B_2, \cdots , B_K\) 的球。
  • 输出当前袋中最小的球的编号。
  • 把拿走的球放回袋子里。

对于每次查询,输出结果。

思路

首先,我们可以发现:\(N\) 范围很大,无法暴力解决;但是 \(K\) 范围很小。
于是,我想到了一个思路:就是可以记录每个小球的编号与上面的数,并依据数的大小升序排列
我们使 \(minn_i\) 为第 \(i\) 小的球的编号。
然后对于每次的查询,都遍历 \(minn\) 数组。
如果没有一个拿走的小球编号与它相同,说明找到一个位置
我们就输出它代表的值,并退出循环(因为升序排序,所以这肯定是最小的值)。
由于 \(K\) 的范围非常小,所以并不会超时。

代码部分

#include <bits/stdc++.h>
using namespace std;
struct node
{
	int num, s;
}a[300010], minn[300010];
bool cmp(node a, node b)
{
	return a.num < b.num;
}
int n, Q, k;
int b[10];
int main()
{
	cin >> n >> Q;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i].num;
		a[i].s = i;
	}
	sort(a + 1, a + 1 + n, cmp);
	for(int i = 1;i <= n;i ++)//进行编号
		minn[i] = a[i];
	while(Q --)
	{
		cin >> k;
		for(int i = 1;i <= k;i ++)
			cin >> b[i];
		int ans = 0;
		for(int i = 1;i <= n;i ++)
		{
			bool fg = 0;
			for(int j = 1;j <= k;j ++)
				if(minn[i].s == b[j])//这个小球被拿走了
				{
					fg = 1;
					break;
				}
			if(fg)
				continue;
			else //这个小球是当前最小的
			{
				ans = minn[i].num;
				break;
			}
		}
		cout << ans <<endl;
	}
	return 0;
}

D:Integer-duplicated Path

题意

一棵树有 \(N\) 个节点,\(N - 1\) 条边。第 \(i\) 个节点上写着数字 \(A_i\)
我们希望知道,对于 \(k = 1, 2, \cdots , N\),能否在一条从节点 \(1\) 到节点 \(k\)简单路径中出现两个相同的整数。

思路

这题是树论,我的解决思想有点像树形 DP 的思路。
首先,我们用 \(vis\) 数组记录节点是否到过。然后,用 \(sum\) 数组记录在路径中出现的每个整数的数量,如 \(sum_i\) 代表在这条路径中数字 \(i\) 的出现次数。
由于数字大小范围比较大,所以需要map 来实现
然后,以节点 \(1\) 作为起点,一路向下遍历。遍历的过程中不断记录整数的出现次数。如果有一个整数的出现次数大于等于了两次,我们就需要记录这个节点是有答案的。但是直接查询时间复杂度太大了,我这里用了一个转化思想来实现。
对于节点 \(u\),设其父节点为 \(fa\)。如果设 \(maxn_i\) 为从节点 \(1\) 到节点 \(i\)路径上出现的重复整数的最多数量,就可以发现,对于节点 \(u\),有转移公式:

\[maxn_u = max(maxn_{fa}, sum_{A_u}) \]

然后,我们就可以通过这个来找寻答案了。
还有要注意的是,要在走到一条路径的末尾时,在回溯过程中把用过的数字删掉
然后,这题就算完成了。

代码部分

#include <bits/stdc++.h>
using namespace std;
int n;
int a[200010], ans[200010], maxn[200010];
bool vis[200010];
vector<int> g[200010];
map<int, int> sum;
void dfs(int u, int fa)
{
	if(vis[u])
		return;
	vis[u] = 1;//标注已到达
	sum[a[u]] ++;//增加数字出现次数
	maxn[u] = max(maxn[fa], sum[a[u]]);//转移公式
	if(maxn[u] >= 2)//这个节点可以
		ans[u] = 1;
	for(int i = 0;i < g[u].size();i ++)
	{
		int v = g[u][i];
		dfs(v, u);
	}
	sum[a[u]] --;//回溯
}
int main()
{
	cin >> n;
	for(int i = 1;i <= n;i ++)
		cin >> a[i];
	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);
	for(int k = 1;k <= n;k ++)
	{
		if(ans[k])
			cout << "Yes" << endl;
		else
			cout << "No" <<endl;
	}
	return 0;
}

posted on 2026-03-07 23:40  Leo_29  阅读(9)  评论(0)    收藏  举报

导航