Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)
A
给一个长度为
solution
三个数字的 MEX 值只可能等于
当
所以当且仅当
code
// Date: 2025-08-07
// Time: 22:37:15
void ChatGptDeepSeek()
{
int n;
cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i++)
cin >> a[i];
if(count(a.begin() + 1, a.end(), 0)){
cout << "NO\n";
return;
}
int mx = *max_element(a.begin() + 1, a.end());
for(int i = 1; i <= n; i++){
if(a[i] != -1 && a[i] != mx){
cout << "NO\n";
return;
}
}
cout << "YES\n";
}B
有一个长度为
在每一天开始的时候 Mani 会选择一个空格子,并在这个格子上建一堵墙。
然后 Hamid 会选择一个方向,如果这个方向上没有格子了,那么他可以离开,否则他会摧毁这个方向上离他最近的墙,这一天结束时他会呆在这个地方。
Hamid 想早点离开,Mani 不想让 Hamid 早点离开。如果两人都足够聪明,Hamid 需要多少天才能离开?
solution
如果当前一堵墙都没有,那么 Mani 只能在一个方向建墙,所以 Hamid 一天就可以离开。
让答案为
但如果只考虑了这个,我喜提了 Wa on test 2。
因为 Hamid 其实是可以瞬移到离他最近的墙,我们可以找到离他左边和右边最近的墙,分别记它们的位置为
因为假如
code
// Date: 2025-08-07
// Time: 22:48:46
void ChatGptDeepSeek()
{
int n, x;
cin >> n >> x;
string s; cin >> s;
int ans = min(x, n - x + 1);
s = " " + s;
int L = 0, R = n + 1;
for(int i = x - 1; i >= 1; i--){
if(s[i] == '#'){
L = i;
break;
}
}
for(int i = x + 1; i <= n; i++){
if(s[i] == '#'){
R = i;
break;
}
}
ans = min(ans, max(L + 1, n - R + 1 + 1));
cout << ans << '\n';
}C
有两个长度为
Ali 和 Bahamin 在玩一个游戏,持续
首先,Ali 选择两个下标
和 ( ); 然后 Bahamin 可以重新排列
,交换几个数字的值,也可以让这几个数字保持不变。
在
solution
hint 1
轮游戏后, 不可能减小,因为如果减小,Bahamin 肯定不操作,那值起码不会变。 hint 2
Ali 只会选择一对不同的
,Bahamin 也最多只有第一次需要进行操作。Bahamin 一定可以使得操作后,可以达到最大的 。
所以我们只需要去找一对
想一下,假如我们有四个数字,
其实就是排列组合嘛,其中最大的一定是
由于可以自由交换,我们可以交换
那么对于
它们四个的关系只能是
v2 v4
v1 v3或者
v3 v4 or v3 v4
v1 v2 v2 v1对于下面的两种,这四个数字的贡献已经是
对于第一种,其对答案的贡献为
所以若无使得答案不变的
若不存在
code
// Date: 2025-08-07
// Time: 23:25:18
void ChatGptDeepSeek()
{
int n, m;
cin >> n >> m;
vector<pair<int, int>> a(n + 1);
for(int i = 1; i <= n; i++) cin >> a[i].first;
for(int i = 1; i <= n; i++) cin >> a[i].second;
ll ans = 0;
for(int i = 1; i <= n; i++){
if(a[i].first < a[i].second)
swap(a[i].first, a[i].second);
ans += a[i].first - a[i].second;
}
int add = INF;
sort(a.begin() + 1, a.end(), [](pair<int,int> x, pair<int, int> y){
return x.first < y.first;
});
for(int i = 2; i <= n; i++){
auto [x, y] = a[i];
if(a[i - 1].first >= y){
cout << ans << "\n";
return;
}
add = min(add, y - a[i - 1].first);
}
cout << ans + 2 * add << '\n';
}D
有
问有多少种排列房子的方式,使得有桥连接的房子都在河的两边,且所有桥之间不会交叉。
solution
hint 1 桥的数量一定只能是
,否则就会存在环,有环的话就百分百不行。 hint 2 一个节点的子节点中,不是叶子的节点最多只能有两个。
我赛时没写出这题,之后也是又想了挺久才想明白。我当时是直接先确定树的根节点,然后每个点的子节点中的叶子节点可以自由排列。然后我想的非常混乱了,我找一个有两个不是叶子的子节点的点作为根节点,然后 dfs,乘每个点的非叶子子节点数量的排列。。。但显然这是错的,而且我到最后也越写越不知道怎么确定根节点,也不能确定什么时候需要乘2。
我想的是,根节点有时候会有2个不是叶子节点的子节点,有时候是1个,这让我很不好算答案啊。
但是看了题解才发现一个有用的事情,如果去掉所有的叶子节点,剩下的部分一定是一条链。
这条链一定是相邻的节点在河的不同边,所以最左边的节点其实是可以确定的,每个点的叶子节点的数量都可以自由排列,所以直接算就好了。画个图就会非常的清晰了。
如果这条链上至少有两个节点,那么则可以左右对称一下,答案可以乘2。
#include<bits/stdc++.h>
using namespace std;
using LL = long long;
constexpr int mod = int(1e9) + 7;
constexpr int N = int(2e5);
int fact[N + 1];
void solve()
{
int n, m;
cin >> n >> m;
vector<vector<int>> g(n + 1);
vector<int> d(n + 1);
for(int i = 1; i <= m; i++){
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
d[u]++, d[v]++;
}
if(m != n - 1){
cout << "0\n";
return;
}
int ans = 2, cnt = 0;
for(int u = 1; u <= n; u++){
int leaf = 0, not_leaf = 0;
for(int v : g[u]){
if(d[v] == 1) leaf++;
else not_leaf++;
}
if(d[u] > 1) cnt++;
if(not_leaf > 2){
ans = 0; break;
}
ans = 1LL * ans * fact[leaf] % mod;
}
if(cnt > 1) ans = 2 * ans % mod;
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
fact[0] = 1;
for(int i = 1; i <= N; i++)
fact[i] = 1LL * fact[i - 1] * i % mod;
int T; cin >> T; while(T--)
{solve();} return 0;
}