2025多校CSP模拟赛1

2025多校CSP模拟赛1

开 T1 水,开 T2 发现能乱搞,搞完发现是正确的。

开 T3 发现是熟悉的 dp,马上开写一个插板。

写了 2h 后发现占地面积不好算,放弃了。

T1 交友

发现只要特判类似

CG
GC

即可。

code
// ubsan: undefined
// accoders
#include <iostream>
#include <queue>

using namespace std;

#define emp emplace_back

const int N = 1000 + 10, M = 1e5, K = 20;

#define fi first
#define se second

// #define int long long
using pii = pair <int, int>;
using ll = long long;

char a[N][N];
int ans = 0, cnt[N][N][3];

signed main()
{
    freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
    freopen("friend.in", "r", stdin); freopen("friend.out", "w", stdout);
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j];
    for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++)
    {
        if (a[i][j] != 'G') continue;
        int s = (a[i - 1][j] == 'C') + (a[i][j - 1] == 'C') + (a[i + 1][j] == 'C') + (a[i][j + 1] == 'C');
        cnt[i][j][1] = (a[i][j - 1] == 'C') && (a[i + 1][j] == 'C');
        cnt[i][j][2] = (a[i][j + 1] == 'C') && (a[i + 1][j] == 'C');
        cnt[i][j][0] += s * (s - 1) / 2 - cnt[i][j][1] - cnt[i][j][2];
        if (cnt[i][j][0])
        {
            ++ans;
            continue;
        }
        if (!cnt[i][j][1] && !cnt[i][j][2]) continue;
        if (!cnt[i][j][1])
        {
            ++ans;
            cnt[i + 1][j + 1][0]--;
        }
        else if (!cnt[i][j][2])
        {
            ++ans;
            cnt[i + 1][j - 1][0]--;
        }
        else
        {
            cerr << "?";
        }
    }
    cout << ans << '\n';

    return 0;
}

T2 炼金

因为环一定不优,所以出现环直接爆了,但是感觉写 \(dfs\) 很没有前途,于是选择递推模拟这样的过程。

具体的每次二分一个答案,然后进行很多轮,每轮遍历数组如果欠了金属就找两个儿子要。

容易发现如果 \(O(n)\) 轮后还是还不上,就说明出现了环,此时直接判凑不成。

那么复杂度是 \(O(Tn^2\log V)\)

code
// ubsan: undefined
// accoders
#include <iostream>
#include <queue>

using namespace std;

#define emp emplace_back

const int N = 1000 + 10, M = 1e5, K = 20;

#define fi first
#define se second

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

int n, d[N], ls[N], rs[N], tmpd[N];

signed main() {
    freopen("data.in", "r", stdin);
    freopen("ans.out", "w", stdout);
    freopen("alchemy.in", "r", stdin);
    freopen("alchemy.out", "w", stdout);
    ios ::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    for (int ajdkfjjflsjl = 1; ajdkfjjflsjl <= T; ajdkfjjflsjl++) {
        int s = 0;
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> ls[i] >> rs[i];
        for (int i = 1; i <= n; i++) cin >> d[i], s += d[i], tmpd[i] = d[i];
        int l = 0, r = s, pos = 0;
        while (l <= r) {
            int mid = (l + r) >> 1, tot = 0;
            d[1] -= mid;
            while (1) {
                bool flag = 1;
                ++tot;
                for (int i = 1; i <= n; i++) {
                    if (d[i] < -s) {
                        tot = 0x3f3f3f3f;
                        break;
                    }
                    if (d[i] < 0) {
                        flag = 0;
                        int t = d[i];
                        d[i] = 0;
                        d[ls[i]] += t, d[rs[i]] += t;
                    }
                }
                if (flag)
                    break;
                if (tot > 20)
                    break;
            }
            bool ch = 1;
            for (int i = 1; i <= n; i++) ch &= (d[i] >= 0);
            if (ch)
                pos = mid, l = mid + 1;
            else
                r = mid - 1;
            for (int i = 1; i <= n; i++) d[i] = tmpd[i];
        }
        cout << "Case #" << ajdkfjjflsjl << ": " << pos << '\n';
    }

    return 0;
}

T3 磁铁

发现肯定是先让所有磁铁紧密排列,然后求出剩下的空位进行插板法。

但是左右端点的磁力占地面积是不全的,不过根本没必要枚举左右端点,只需要将占地面积压入 \(dp\) 状态就好。

其次我们发现当一个磁铁需要放入两个磁铁之间时,需要将这两个磁铁分开,这样贡献不好算。

那么就可以枚举当前分为多少个连通块,这样每个联通块的左右端点的磁力就不用考虑了,此时如果从小到大枚举,占地面积的增加就极好算了。

\(dp_{i,j,k}\) 表示当前枚举到 \(i\) 分为 \(j\) 个连通块,占地面积 \(k\)

\(i\) 个单开一个连通块:\(dp_{i,j,k}+=j\times dp_{i-1,j-1,k-1}\)

\(i\) 个合并两个连通块:\(dp_{i,j,k}+=j\times dp_{i-1,j+1,k-(2r_i-1)}\)

\(i\) 个放在一个连通块的左端或右端:\(dp_{i,j,k}+=2j\times dp_{i-1,j,k-r_i}\)

答案就是简单插板法,\(dp_{n,1,i}C^{l-i+n}_{n}\)

复杂度 \(O(n^2l)\)

code
// ubsan: undefined
// accoders
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

#define emp emplace_back

const int N = 60, M = 1e4 + 100, mod = 1e9 + 7;

#define fi first
#define se second

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

int dp[N][N][M], r[N], n, l, C[M][N];

signed main() {
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    freopen("magnet.in", "r", stdin);
    freopen("magnet.out", "w", stdout);
    ios ::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    cin >> n >> l;
    for (int i = 1; i <= n; i++) cin >> r[i];
    sort(r + 1, r + 1 + n);
    dp[0][0][0] = 1;
    C[0][0] = 1;
    for (int i = 1; i <= l + n; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= n; j++) {
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
        }
    }
    for (int i = 1; i <= n; i++)  // now pos
    {
        for (int j = 1; j <= n; j++)  // j SCC
        {
            for (int k = 1; k <= l; k++)  // not empty size
            {
                dp[i][j][k] += dp[i - 1][j - 1][k - 1] * j;  // new
                if (k >= r[i])
                    dp[i][j][k] += dp[i - 1][j][k - r[i]] * 2 * j;  // PushBack or PushFront
                if (k >= 2 * r[i] - 1)
                    dp[i][j][k] += j * dp[i - 1][j + 1][k - 2 * r[i] + 1];  // Merge
                dp[i][j][k] %= mod;
            }
        }
    }
    int ans = 0;
    // cerr << dp[2][2][2] << ' ';
    for (int i = 0; i <= l; i++) {
        // cerr << dp[n][1][i] << ' ';
        ans = (ans + dp[n][1][i] * C[l - i + n][n]) % mod;
    }
    cout << ans << '\n';

    return 0;
}

T4 铁轨

很好的欧拉回路题。

考虑可以将问题转化为加速不消耗代价,减速消耗代价。

那么因为要经过所有的铁轨,那么这些边一定经过。

此时如果将每个速度抽象成一个点,并且加入一个从 \(inf\) 连向 \(1\) 个边,这样就不用考虑起点终点,由路径变为回路。

因为向左经过一个点和向右经过一个点的数量一定要相同,那么就以此为代价在 \(i\)\(i+1\) 间连边。

可是有可能连完后不连通,那么可以上一个最小生成树维护一下。

code
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;

#define emp emplace_back

const int N = 1e6 + 10, K = 102, mod = 1e9 + 7;

#define fi first
#define se second

#define int long long
using pii = pair <int, int>;
using ll = long long;
const int inf = 0x3f3f3f3f;

int f[N], s[N], t[N], a[N << 2], sum[N << 2], L[N], R[N];

int Find(int x)
{
    if (f[x] == x) return f[x];
    return f[x] = Find(f[x]);
}

void Merge(int x, int y)
{
    int xx = Find(x), yy = Find(y);
    if (xx == yy) return ;
    f[xx] = yy;
    L[yy] = min(L[yy], L[xx]);
    R[yy] = max(R[yy], R[xx]);
}

struct Node
{
    int from, to, w;
}e[N];

signed main()
{
    // freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);
    // freopen("rail.in", "r", stdin); freopen("rail.out", "w", stdout);
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);

    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i] >> t[i];
        a[i] = s[i], a[i + n] = t[i];
    }
    a[2 * n + 1] = a[2 * n + 2] = s[n + 1] = t[n + 1] = 1;
    ++n;
    sort(a + 1, a + 1 + n * 2);
    int cnt = unique(a + 1, a + 1 + n * 2) - (a + 1);
    for (int i = 1; i <= cnt; i++) f[i] = L[i] = R[i] = i;
    for (int i = 1; i <= n; i++)
    {
        s[i] = lower_bound(a + 1, a + 1 + cnt, s[i]) - a;
        t[i] = lower_bound(a + 1, a + 1 + cnt, t[i]) - a;
        sum[s[i]]++, sum[t[i]]--;
        Merge(s[i], t[i]);
    }
    sum[1]--;
    for (int i = 1; i <= cnt; i++) sum[i] = sum[i - 1] + sum[i];
    int ans = 0;
    for (int i = 2; i <= cnt; i++)
    {
        ans += max(0ll, (a[i] - a[i - 1]) * sum[i - 1]);
    }
    int tot = 0;
    for (int i = 2; i <= cnt; i++)
    {
        if (sum[i - 1] != 0) Merge(i, i - 1);
        if (Find(i) != Find(i - 1)) e[++tot] = {i - 1, i, a[i] - a[i - 1]};
    }
    sort(e + 1, e + 1 + tot, [](Node x, Node y) {return x.w < y.w;});
    for (int i = 1; i <= tot; i++)
    {
        int x = Find(e[i].from), y = Find(e[i].to);
        if (x == y) continue;
        f[x] = y;
        ans += e[i].w;
    }
    cout << ans;

    return 0;
}
posted @ 2025-10-04 16:13  QEDQEDQED  阅读(39)  评论(5)    收藏  举报