跳转至

第三场

2025“钉耙编程”中国大学生算法设计春季联赛(3)

1001

明天补。

看了好久的。。幸好有题解且能看别人代码帮助我理解。

Lucas定理: https://oi-wiki.org/math/number-theory/lucas/ $$ \tbinom{x}{y}\equiv\tbinom{\lfloor x/p\rfloor}{\lfloor y/p\rfloor}\tbinom{x\%p}{y\%p}\mod p $$ 题目要求出满足以下条件的方案数 $$ \prod_{i=1}^{n} \binom{a_i}{b_i} \mod 2 = 1 $$ 所以我们需要每个 \(\binom {a_i}{b_i}=1\) ,即 \(a_i\)\(b_i\) 的二进制的每一位的组合数都为 \(1\) ,也就是 \(a_i\)\(0\) 的位, \(b_i\) 不能为 \(1\) ,其他的都可以。

但是有 \(b_i\le Li\) 的条件,就很烦。

如果 \(Li>=a_i\) 显然所有方案都能满足。

否则我们看 \(a_i\)\(L_i\) 的二进制位

\(L_i\) 的这一位为 \(1\)\(a_i\) 的这一位也为 \(1\) 那么可以让 \(a_i\) 的这一位为 \(0\) ,后面则可以自由的选择所有方案。

\(L_i\) 的这一位为 \(1\)\(a_i\) 的这一位为 \(0\) 那么我们可以取得后面的所有数字,就可以直接跳出循环了。

为什么可以跳出循环呢?因为我们实际上,枚举到每一个位,我们加的方案数都是,\(b_i\) 的前面的位和 \(L_i\) 的前面是一模一样的,而这一位等于 \(0\) ,就可以满足 \(b_i\le L_i\) 的条件。而后面的位就可以随便取了。若 \(a_i\) 的这一位等于 \(0\) ,则我们之后不能保证前面全部相等了,所以需要退出循环。

但是由于每次我们都设置一位等于 \(0\) , 若我们遇到的所有 \(L_i\)\(1\) 的位, \(a_i\) 也为 \(1\) ,则实际上会少考虑 \(b_i=L_i\) 的方案,所以需要给方案数 +\(1\)

最终答案即为每一个 \(b_i\) 的方案数的乘积。

constexpr int mod = 998244353;

void ChatGptDeepSeek()
{
    /*
    101
    101

    2+1
    */
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    ll ans = 1;
    for (int i = 1; i <= n; i++)
    {
        int L;
        cin >> L;
        if (L >= a[i])
        {
            int x = 0;
            for (int j = 30; j >= 0; j--)
                x += (a[i] >> j & 1);
            ans = ans * (1 << x) % mod;
            continue;
        }
        int cnt = 0, ok = 1;
        for (int j = 30; j >= 0; j--)
        {
            int x = a[i] >> j & 1;
            int y = L >> j & 1;
            if (y)
            {
                int bit = 0;
                for (int k = j - 1; k >= 0; k--)
                    bit += (a[i] >> k & 1);
                cnt = (cnt + (1 << bit)) % mod;
                if (x == 0)
                {
                    ok = 0;
                    break;
                }
            }
        }
        if (ok)
            cnt++;
        ans = ans * cnt % mod;
    }
    cout << ans << '\n';
}

1003

看题解还没太看明白。。原来这么妙的。。

统计对于每一个公司,不满足的能力的数量,若全部满足,则是可以下一次面试的公司。把所有的不满足的能力的公司,放进 \(m\) 维的优先队列,当我们每次能力更新时,去检查对应的能力是否满足。

using pii = pair<int, int>;
using ll = long long;

void ChatGptDeepSeek()
{
    int n, m;
    cin >> n >> m;
    vector<ll> a(m + 1);
    for (int i = 1; i <= m; i++)
        cin >> a[i];
    vector<vector<int>> c(n + 1, vector<int>(m + 1)), w(n + 1, vector<int>(m + 1));
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
            cin >> c[i][j];
        for (int j = 1; j <= m; j++)
            cin >> w[i][j];
    }
    vector<priority_queue<pii, vector<pii>, greater<>>> pq(m + 1);
    queue<int> q;
    vector<int> cnt(n + 1);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (c[i][j] > a[j])
            {
                pq[j].push({c[i][j], i});
                cnt[i]++;
            }
        }
        if (!cnt[i])
            q.push(i);
    }
    int ans = 0;
    while (!q.empty())
    {
        ans++;
        int k = q.front();
        q.pop();
        for (int i = 1; i <= m; i++)
        {
            a[i] += w[k][i];
            while (!pq[i].empty() && a[i] >= pq[i].top().first)
            {
                auto [_, j] = pq[i].top();
                pq[i].pop();
                cnt[j]--;
                if (!cnt[j])
                    q.push(j);
            }
        }
    }
    cout << (ans == n ? "YES\n" : "NO\n");
}

怎么说呢?我本来还在想,他是怎么用到优先队列,怎么更新能力值的。。。非常的牛。

1005

由于城市间是互相可以到达的,所以只需要求联通块的数量。

可以用 DFS 或 并查集。

constexpr int N = 3e5 + 10;
int f[N + 1];

int find(int x)
{
    // cerr << x << " " << f[x] << '\n';
    return f[x] == x ? x : f[x] = find(f[x]);
}

void ChatGptDeepSeek()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
        f[i] = i;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        // cerr << i << "\n";
        if (i - a[i] >= 1) {
            // cerr << i - a[i] << " " << find(i - a[i]) << '\n';

            f[find(i)] = find(i - a[i]);
            // f[i] = f[find(f[i - a[i]])];
        }
        if (i + a[i] <= n) {
            // cerr << i + a[i] << " " << find(i + a[i]) << '\n';
            f[find(i + a[i])] = find(f[i]);
            // f[i] = f[find(f[i + a[i]])];
        }

        // for (int j = 1; j <= n; j++)
        //     cerr << f[j] << " \n"[j == n];
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (find(f[i]) == i)
            ans++;
        // cerr << f[i] << '\n';
    }
    cout << ans - 1 << '\n';
}

1010

\(\mid x_0-x_1\mid+\mid y_0-y_1\mid=max(x_0-x_1+y_0-y_1,\)

\(x_1-x_0+y_0-y_1,x_0-x_1+y_1-y_0,x_1-x_0+y_1-y_0)\)

所以到 \(x_0,y_0\) 曼哈顿距离最大的点,一定是使得对应的 \(x_1+y_1\)\(x_1-y_1\) 更大/更小。

void ChatGptDeepSeek()
{
    int n, m;
    cin >> n >> m;
    vector<int> x(n), y(n);
    vector<int> a(4);
    a[1] = a[3] = 2e9;
    a[0] = a[2] = -2e9;
    for (int i = 0; i < n; i++)
    {
        cin >> x[i] >> y[i];
        a[0] = max(a[0], x[i] + y[i]);
        a[1] = min(a[1], x[i] + y[i]);
        a[2] = max(a[2], x[i] - y[i]);
        a[3] = min(a[3], x[i] - y[i]);
    }
    long long ans = 4e9;
    for (int i = 0; i < m; i++)
    {
        int X, Y;
        cin >> X >> Y;
        long long c = X + Y, d = X - Y;
        long long now = max({c - a[1], a[0] - c, a[2] - d, d - a[3]});
        ans = min(ans, now);
    }
    cout << ans << '\n';
}