Skip to content

Atto Round 1 (Codeforces Round 1041, Div. 1 + Div. 2)

A

给一个长度为 n 的数组,是否可以通过把其中的 1 更换成任意数字,使得对于 1in2 :

mex(ai,ai+1,ai+2)=max(ai,ai+1,ai+2)min(ai,ai+1,ai+2)

solution

三个数字的 MEX 值只可能等于 0,1,2,3

mex>0 时,min(ai,ai+1,ai+2)=0,于是 max(ai,ai+1,ai+2)=mex,那么显然对于 mex=x 的数组,其中肯定不能出现值等于 x 的元素。

所以当且仅当 mex(ai,ai+1,ai+2)=0 时可以满足条件,所以 a 中所有元素必须全部相等且不等于 0 才可以是一个好数组。

code

cpp
// 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

有一个长度为 n 的通道,有 n 个格子,最左边的是第 1 个,刚开始时 Hamid 在第 x 个格子。有一些格子是空的,有一些格子是墙。

在每一天开始的时候 Mani 会选择一个空格子,并在这个格子上建一堵墙。

然后 Hamid 会选择一个方向,如果这个方向上没有格子了,那么他可以离开,否则他会摧毁这个方向上离他最近的墙,这一天结束时他会呆在这个地方。

Hamid 想早点离开,Mani 不想让 Hamid 早点离开。如果两人都足够聪明,Hamid 需要多少天才能离开?

solution

如果当前一堵墙都没有,那么 Mani 只能在一个方向建墙,所以 Hamid 一天就可以离开。

让答案为 ans,那么显然 ansmin(x,nx+1),因为如果啥都不管只往一个方向走,肯定至少能达到这个数字。

但如果只考虑了这个,我喜提了 Wa on test 2。

因为 Hamid 其实是可以瞬移到离他最近的墙,我们可以找到离他左边和右边最近的墙,分别记它们的位置为 L,R。Hamid 一定可以用一天到达 L,或 R。那么 ansmax(L+1,nR+2)

因为假如 L+1 更小,那么 Mani 肯定会在 x1 建墙,使得你无法瞬移到 L,反之他会在 x+1 建墙,所以只能取 max。

code

cpp
// 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

有两个长度为 n 的数组 a,b

Ali 和 Bahamin 在玩一个游戏,持续 k 回合。在每回合中:

  • 首先,Ali 选择两个下标 ij (1i<jn);

  • 然后 Bahamin 可以重新排列 ai,aj,bi,bj,交换几个数字的值,也可以让这几个数字保持不变。

k 轮游戏之后,游戏的得分为 v=i=1n|aibi|. Ali 希望 v 尽量小,Bahamin 希望 v 尽量大,并且两个人都足够聪明,最终的 v 是多少。

solution

  • hint 1

    k 轮游戏后,v 不可能减小,因为如果减小,Bahamin 肯定不操作,那值起码不会变。

  • hint 2

    Ali 只会选择一对不同的 (i,j),Bahamin 也最多只有第一次需要进行操作。Bahamin 一定可以使得操作后,可以达到最大的 v

所以我们只需要去找一对 (i,j) 使得进行操作后,v 的增加值尽量小。

想一下,假如我们有四个数字,v1v2v3v4,我们想要假设它们是我们选的 ai,aj,bi,bj 那四个数字,操作之后 |aibi|+|ajbj| 的值可能是什么呢?

其实就是排列组合嘛,其中最大的一定是 v3+v4v1v2。也就是我们交换后,不要让最大的两个数字的下标相同,就可以使得答案变得最大。

由于可以自由交换,我们可以交换 ai,bi,使得 ai>=bi,然后我们按 a 的值递增排序。

那么对于 i<j,一定有 aiaj,bjaj,bibj。所以 aj 一定是这几个数字中最大的。

它们四个的关系只能是

v2 v4
v1 v3

或者

v3 v4 or v3 v4
v1 v2    v2 v1

对于下面的两种,这四个数字的贡献已经是 v4+v3v2v1,所以不会让答案变大。假设存在满足这种关系的 (i,j) 那么答案不会变。

对于第一种,其对答案的贡献为 v4+v2v3v1。Bahamin 交换之后,答案的变化为 (v4+v3v2v1)(v4+v2v3v1)=2(v3v2)

所以若无使得答案不变的 (i,j),交换并排序之后,b 也一定是一个有序数组。

若不存在 1 的情况,v 会增加 2min1i<jn(bjai)

code

cpp
// 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

n 个房子 m 座桥,这 n 个房子可以通过这些桥联通。

问有多少种排列房子的方式,使得有桥连接的房子都在河的两边,且所有桥之间不会交叉。

solution

  • hint 1 桥的数量一定只能是 n1,否则就会存在环,有环的话就百分百不行。

  • hint 2 一个节点的子节点中,不是叶子的节点最多只能有两个。

我赛时没写出这题,之后也是又想了挺久才想明白。我当时是直接先确定树的根节点,然后每个点的子节点中的叶子节点可以自由排列。然后我想的非常混乱了,我找一个有两个不是叶子的子节点的点作为根节点,然后 dfs,乘每个点的非叶子子节点数量的排列。。。但显然这是错的,而且我到最后也越写越不知道怎么确定根节点,也不能确定什么时候需要乘2。

我想的是,根节点有时候会有2个不是叶子节点的子节点,有时候是1个,这让我很不好算答案啊。

但是看了题解才发现一个有用的事情,如果去掉所有的叶子节点,剩下的部分一定是一条链。

这条链一定是相邻的节点在河的不同边,所以最左边的节点其实是可以确定的,每个点的叶子节点的数量都可以自由排列,所以直接算就好了。画个图就会非常的清晰了。

如果这条链上至少有两个节点,那么则可以左右对称一下,答案可以乘2。

cpp
#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;
}