『模拟赛题解』9.27 NOIP 模拟赛

9.27 NOIP 模拟赛

T1. 小 O 的珠子

Description

小 O 有一些很漂亮的珠子,根据小 O 对珠子的喜欢程度,编号为 a 到 z 。 珠子们之间用魔力相互吸引,排列成一条线。

有一天,小 Y 乱丢法术,一不小心把某些珠子之间的魔力消除了,珠子们断成了 \(n\) 条。

现在,小 O 想知道,将断开的 \(n\) 条珠子们重新排列,能得到的字典序最小的序列是什么。

Solution

赛时思路

直接将所有字符串 sort 一遍。

实际得分 \(0\) pts。

正解

那上述思路哪里错了呢 ?

错就错在排序的规则。

对于 \(s_1, s_2\),如果 \(s_1,s_2\) 字典序比 \(s_2,s_1\) 小,我们定义为 \(s_1 < s_2\)。显然,如果 \(s_1 < s_2\),则交换 \(s_1, s_2\) 更优。

所以排序时让 \(s_1 + s_2\)\(s_2 + s_1\) 比较。

Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2e5 + 5;
int n;
string s[maxn];
string ans;

bool cmp(string x, string y)
{
    return x + y < y + x;
}

int main()
{
	freopen("bead.in", "r", stdin);
	freopen("bead.out", "w", stdout);
	cin >> n;
	for (int i = 1; i <= n; i ++)
		cin >> s[i];
	sort (s + 1, s + n + 1, cmp);
	for (int i = 1; i <= n; i ++)
		ans += s[i];
	cout << ans;
	return 0;
 }

T2. 王国的传送门

Description

一个王国有 \(n\) 个城镇,编号为 \(1\)\(n\)。城镇 \(1\) 是首都。

王国中的每个城镇都有一个单向传送门,可以将人从一个城镇快速传送到另一个城镇。

城镇 \(i\) 的传送目标是城镇 \(a_i(1 \le a_i \le n)\)。通过多次使用传送门,可以保证从任何城镇到达首都。

国王洛浔喜欢整数 \(K\) 。洛浔想要调整一些传送门的目的地(出发地不变),以便从任何城镇开始,使用传送门恰好 \(K\) 次后将可以到达首都。并且,洛浔允许这 \(K\) 次中重复使用任意传送门。

请你帮洛浔找到需要调整的传送门的最小数量(允许存在自环)。

Solution

赛时思路

同下。

正解

不难发现,城镇 \(1\) 的传送门只能传送到 \(1\),否则不可能保证 \(1 \to 1\) 使用传送门恰好 \(K\) 次。

证明如下:

\[\begin{flalign} &\text{If: } \\ &1 \to x \\ &\text{For set S:} \\ & ~ ~ ~ ~ ~ S = {a_1, a_2, ..., a_n}: \\ & ~ ~ ~ ~ ~ ~ ~ ~ ~ a_1 \to a_2, a_2 \to a_3, ..., a_n - 1 \to a_n, a_n \to a_1 \\ & ~ ~ ~ ~ ~ x \in S \\ &\text{Because this graph is a tree without } edge(1, x), \text{so}: \\ & ~ ~ ~ ~ ~ 1 \in S \\ &\text{So for each } v \in S: \\ & ~ ~ ~ ~ ~ K - l|S| - dis(1, v) = 0 \\ & ~ ~ ~ ~ ~ l \text{ is an integer from } [0, +\infty] \\ &\text{Apparently its cannot meet all } v \in S \text{ apart from } |S| = 1. \\ &\text{We said that } 1 \in S,so \ 1 \to 1.\\ \end{flalign} \]

所以只需要用 DFS 跑一边,判断每个点到 \(1\) 的距离,如果 \(\ge K\),就将这个点向上走 \(K - 1\) 步的祖先给连向 \(1\)

Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;

int n, k, ans, cnt;
int head[maxn];
struct Edge
{
	int nxt, to;
} edge[maxn];

void addedge(int x, int y)
{
	edge[++ cnt].to = y;
	edge[cnt].nxt = head[x];
	head[x] = cnt;
}

int dfs(int x, int fa)
{
	int ret = 0;
	for (int i = head[x]; i; i = edge[i].nxt)
		ret = max(ret, dfs(edge[i].to, x));
	ret ++;
	if (ret >= k && fa != 1)
	{
		ans ++;
		ret = 0;
	}
	return ret;
}

int main()
{
	freopen("gate.in", "r", stdin);
	freopen("gate.out", "w", stdout);
	cin >> n >> k;
	for (int i = 1; i <= n; i ++)
	{
		int x;
		cin >> x;
		if (i == 1 && x != 1)
			ans = 1;
		else if (i != 1)
			addedge(x, i);
	}
	dfs(1, 1);
	cout << ans;
	return 0;
}

T3. wwk与黑白树

Description

wowaka 给你一棵树,上面有 \(n\) 个节点。一开始所有边都是黑色的。

每次你可以选择树上一条所有边都 是黑色的路径,删掉其中一条边,然后在路径的两个端点之间连一条白色的边。求最后能否得到目标形态(都是白色的边)的树。

Solution

赛时思路

没写。

正解

考虑两棵树合在一起,那么如果两点之间存在两条边(称为 \((x,y)\) ),则可以将度数较小的 \(x\) 的连边全部转移到 \(y\) 上,类似于缩点。

Complexity

时间复杂度:\(O(n \log (n)^2)\)

Code

#include <bits/stdc++.h>
using namespace std;

const int maxn = 5e4 + 5;

int n;
map < pair <int, int>, int> p;
queue < pair <int, int> > q;
multiset <int> v[maxn];

int main()
{
	freopen("wowaka.in", "r", stdin);
	freopen("wowaka.out", "w", stdout);
	int tt;
	cin >> tt;
	while (tt --)
	{
		cin >> n;
		p.clear();
		for (int i = 1; i <= n; i ++)
			v[i].clear();
		while (!q.empty())
			q.pop();
		for (int i = 1; i < 2 * n - 1; i ++)
		{
			int x, y;
			cin >> x >> y;
			if (x < y)
				swap(x, y);
			v[x].insert(y);
			v[y].insert(x);
			p[{x, y}] ++;
			if (p[{x, y}] == 2)
				q.push({x, y});
		}
		int x = 0;
		while (!q.empty())
		{
			if (x >= n - 1)
				break;
			pair <int, int> k;
			k = q.front();
			q.pop();
			if (k.first < k.second)
				swap(k.first, k.second);
			if (!p[k])
				continue;
			if (v[k.first].size() > v[k.second].size())
				swap(k.first, k.second);
			for (auto it = v[k.first].begin(); it != v[k.first].end(); it ++)
			{
				int i = *it;
				v[i].erase(v[i].find(k.first));
				p[{max(i, k.first), min(i, k.first)}] --;
				if (k.second != i)
				{
					v[k.second].insert(i);
					v[i].insert(k.second);
					p[{max(i, k.second), min(i, k.second)}] ++;
					if (p[{max(i, k.second), min(i, k.second)}] == 2)
						q.push({max(i, k.second), min(i, k.second)});
				}
			}
			v[k.first].clear();
			x ++;
		}
		cout << (x >= n - 1 ? "YES\n" : "NO\n");
	}
	return 0;
}

T4. 反复横跳

略。

Others

T2 证明过程:感谢 Pretharp 的贡献。

posted @ 2023-09-28 17:32  Clyfort  阅读(97)  评论(1编辑  收藏  举报