2025.11.11 第十一届中国大学生程序设计竞赛 女生专场

Solved: 9/12

Rank: 5 (现场)/ 12(ucup)


A. 环状线

签到题。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, a, b;
    cin >> n >> a >> b;
    int x = 1;
    if (b < a) swap(a, b), x = 2;
    if (b-a < n-(b-a)) cout << x << '\n';
    else cout << 3-x << '\n';
}

J. 后鼻嘤

签到题。考点:getline(?

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    getline(cin, s);
    s += '\n';
    string cur;
    vector <string> ans;
    for (char ch : s)
    {
        if (ch >= 'a' && ch <= 'z') cur += ch;
        else
        {
            if (cur.back() == 'n') cur += 'g';
            ans.push_back(cur);
            cur = "";
        }
    }
    for (string s : ans) cout << s << ' ';
    cout << '\n';
}

G. 最大公约数

题意

\([1,n]\) 中选出 \(m\) 个整数,使它们两两互质。

题解

选出合数显然不优(会导致和它有公因子的数都选不了),故选择 1 和所有质数即可。

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

const int N = 3e5+5;
bool np[N];
int pri[N], cnt, pre[N];
void sieve(int n)
{
    for (int i=2; i<=n; ++i)
    {
        if (!np[i]) pri[++cnt] = i;
        for (int j=1; j<=cnt && i*pri[j] <= n; ++j)
        {
            np[i*pri[j]] = 1;
            if(!(i%pri[j])) break;
        }
    }
    for (int i=1; i<=n; ++i) pre[i] = pre[i-1] + !np[i];
}

int main()
{
    int n, k; cin >> n >> k;
    sieve(n);
    if (pre[n] < k) cout << "NO\n";
    else
    {
        cout << "YES\n";
        int cnt = 0;
        for (int i=1; i<=n; ++i)
        {
            if (!np[i]) cout << i << ' ', ++cnt;
            if (cnt == k) break;
        }
        cout << '\n';
    }
}

B. 爬山

题意

无向带权图,每个点有高度,从低到高走会积累疲劳值,从高到低会清空疲劳值,在疲劳值不超过 \(H\) 的前提下求最短路。

\(n\leq 10^4, m\leq 2\times 10^4, H\leq 100\)

题解

分层图最短路,复杂度 \(O(mH\log mH)\)

点太多了所以最好不要把图建出来

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

struct node
{
    int p, h;
    ll d;
    node(int p, int h, ll d) : p(p), h(h), d(d) {}
    bool operator < (const node& t) const
    {
        return d > t.d;
    }
};

const int N = 1e4+5;
int n, m, H, h[N];
vector <pii> e[N];
void adde(int x, int y, int z)
{
    e[x].emplace_back(y, z);
}
ll dis[N][105];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> H;
    for (int i=1; i<=n; ++i) cin >> h[i];
    for (int i=1; i<=m; ++i)
    {
        int x, y, z;
        cin >> x >> y >> z;
        adde(x, y, z), adde(y, x, z);
    }
    memset(dis, 0x3f, sizeof(dis));
    priority_queue <node> pq;
    dis[1][0] = 0;
    pq.push(node(1,0,0));
    while (!pq.empty())
    {
        node cur = pq.top(); pq.pop();
        int u = cur.p, r = cur.h; ll w = cur.d;
        if (w > dis[u][r]) continue;
        for (auto & [v, w] : e[u])
        {
            int s = h[v] >= h[u] ? r + h[v] - h[u] : 0;
            if (s <= H && dis[v][s] > dis[u][r] + w)
            {
                dis[v][s] = dis[u][r] + w;
                pq.push(node(v, s, dis[v][s]));
            }
        }
    }
    for (int i=2; i<=n; ++i)
    {
        ll ans = 1e18;
        for (int j=0; j<=H; ++j) ans = min(ans, dis[i][j]);
        if (ans > 1e17) cout << "-1 ";
        else cout << ans << ' ';
    }
}

C. 短视频

题意

\(n\) 个视频,按顺序看。在每一秒:

  • 若当前看视频时间 \(x\leq T\),则无论如何都继续看;

  • 若当前看视频时间 \(x > T\),如果这个视频的吸引值 \(k_i \geq x - T\),则继续看,否则跳到下一个视频。

  • 每个视频至少看 \(1\) 秒。

\(n\leq 10^5, T,t_i\leq 10^9\)

题解

签到题。因为第三个条件藏在最后喜提+5

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N = 1e5+5;
int n;
ll T, t[N], k[N];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> T;
    for (int i=1; i<=n; ++i) cin >> t[i] >> k[i];
    ll ans = 0;
    for (int i=1; i<=n; ++i)
    {
        ans = max(ans + 1, min(ans + t[i], k[i] + T + 1));
    }
    cout << ans << '\n';
}

K. 左儿子右兄弟

题意

给一棵树,可以任意重排子树顺序,求左儿子右兄弟表示下的最小子树大小和,以及达到这个最小值的方案数。

\(n\leq 10^5\)

题解

每个节点的子树重排对答案的贡献是独立的:都是 \(\sum_i i\times sz_{v_i}\)

所以 dfs 一遍处理出所有子树大小后对每个点的儿子排序,然后统计答案即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N = 1e5+5, mod = 998244353;
int n;
ll fac[N];
void init(int n)
{
    fac[0] = 1;
    for (int i=1; i<=n; ++i) fac[i] = fac[i-1] * i % mod;
}
vector <int> e[N];
void adde(int x, int y)
{
    e[x].push_back(y);
}
int sz[N];
void dfs(int u)
{
    sz[u] = 1;
    for (int v: e[u])
    {
        dfs(v);
        sz[u] += sz[v];
    }
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n; init(n);
    for (int i=2; i<=n; ++i)
    {
        int x; cin >> x;
        e[x].push_back(i);
    }
    dfs(1);
    ll ans1 = sz[1], ans2 = 1;
    for (int i=1; i<=n; ++i)
    {
        vector <int> ss;
        for (int j : e[i]) ss.push_back(sz[j]);
        sort(ss.begin(), ss.end()); reverse(ss.begin(), ss.end());
        int len = 1;
        for (int i=0; i<ss.size(); ++i)
        {
            if (ss[i] == ss[i-1]) ++len;
            else ans2 = ans2 * fac[len] % mod, len = 1;
            ans1 += (i+1) * ss[i];
        }
        ans2 = ans2 * fac[len] % mod;
    }
    cout << ans1 << '\n' << ans2 << '\n';
}

E. 购物计划

题意

\(n\) 个商品和 \(m\) 个满减计划,第 \(i\) 个商品原价 \(w_i\) 折扣 \(\frac {p_i}{q_i}\),第 \(j\) 个计划满 \(a_j\)\(b_j\)
对每个商品,你可以选择将它的价格降为 \([\frac {p_i}{q_i}w_i, w_i]\) 后再参与任意一个满减计划。
求每个商品的最低购买价格。

\(n,m\leq 5\times 10^5, a_i, b_i, w_i\leq 10^6\)

题解

最优策略一定是降到某个 \(a_i\) 的倍数,或是降到最低价,再参与满减。可以分别处理。

枚举倍数,预处理 \(g_x\) 表示价格 \(x\) 满减后能到达的最低价。这里得到的只是 \(x\) 恰为某个 \(a_j\) 的倍数时的最低价,但不降到 \(x\) 显然不如继续降。
对每个商品,用 st 表求出 \(g[\lceil \frac {p_i}{q_i}w_i\rceil, w_i]\) 的最小值。

枚举倍数,预处理 \(f_x\) 表示价格 \(x\) 满减后能减少的价格的最大值。
对每个商品,用 st 表求出 \(f[1, \lfloor \frac {p_i}{q_i}w_i\rfloor]\) 的最大值,再由 \(\frac {p_i}{q_i}w_i\) 减去即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N = 5e5+5, M = 1e6+5;
int n, m;
int d[M], f[M], g[M];
int st1[20][M], st2[20][M];
int qry1(int l, int r)
{
    if (l>r) return 0;
    int o = __lg(r-l+1);
    return max(st1[o][l], st1[o][r-(1<<o)+1]);
}
int qry2(int l, int r)
{
    if (l>r) return 1e9;
    int o = __lg(r-l+1);
    return min(st2[o][l], st2[o][r-(1<<o)+1]);
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m;
    for (int i=1; i<=m; ++i)
    {
        int x, y; cin >> x >> y;
        d[x] = max(d[x], y);
    }
    int lim = 1e6;
    for (int i=1; i<=lim; ++i) if (d[i])
        for (int j=1; i*j<=lim; ++j)
            f[i*j] = max(f[i*j], d[i]*j);
    for (int i=1; i<=lim; ++i) g[i] = i - f[i];
    for (int i=1; i<=lim; ++i) st1[0][i] = f[i], st2[0][i] = g[i];
    for (int i=1; i<=19; ++i)
        for (int j=1; j<=lim-(1<<i)+1; ++j)
        {
            st1[i][j] = max(st1[i-1][j], st1[i-1][j + (1<<i-1)]);
            st2[i][j] = min(st2[i-1][j], st2[i-1][j + (1<<i-1)]);
        }
    for (int i=1; i<=n; ++i)
    {
        int x, p, q; cin >> x >> p >> q;
        int l = 1ll * x * p / q, r = (1ll * x * p + q-1) / q;
        int L = l - qry1(1, l), R = qry2(r, x);
        if (R <= L) cout << R << ' ' << 1 << '\n';
        else
        {
            int u = 1ll * x * p - 1ll * l * q, v = q;
            int g = __gcd(u, v);
            u /= g, v /= g;
            cout << 1ll * L * v + u << ' ' << v << '\n';
        }
    }
}

F. 丝之歌

题意

  • \(n\leq 10^3\) 个关卡,\(m\leq 10^3\) 种敌人,满血 \(c\leq 10\)

  • 除最后一关外,每获得 \(a\) 枚钱会在过关时自动存储为一串,到最后一关后每串钱换 \(b\) 枚钱。

  • \(i\) 个关卡会依次出现 \(k_{i,j}\leq 10^9\) 个第 \(id_{i,j}\) 种敌人,\(j = 1\dots t_i, t_i\leq 100\)

  • \(i\) 个敌人在你血量为 \(x\) 时有 \(p_{i,x,y}\) 的概率使你的血量变为 \(y\),血量为 \(0\) 时你需要重打这一关同时失去所有未存储的钱。游戏无限重生且不存在死档,可以理解为只有一次通过和一次未通过两种情况。

  • 求通过所有关卡后的期望钱数。

题解

矩乘 + 期望dp 二合一。

对关卡,我们关心的只是满血进关后死亡的概率。这个概率就是矩阵 \(\prod_{j} P_{id_{i,j}}^{k_{i,j}}\) 的第 \(c\)\(0\) 列。矩阵快速幂复杂度太高会 T,加预处理 \(2^k\) 次幂的优化即可。
这部分时间复杂度为 \(O(mc^3\log k + ntc^2\log k)\)。(不优化是 \(O(ntc^3\log k)\) 可能也能卡过,没试)

设上面的死亡概率为 \(p_0\),令 \(p_1 = 1-p_0\),设 \(f(i,j)\)\(g(i,j)\) 表示第 \(i\) 关通关、未存储的钱数为 \(j\) 时的概率和期望钱串数,则有转移

\[\begin{aligned} f(i, r) & \leftarrow f(i-1, j)p_0; \\ g(i, r) & \leftarrow f(i-1, j)(g(i-1, j) + q)p_1; \\ f(i, (j+r)\bmod a) & \leftarrow f(i-1, j)p_1; \\ g(i, (j+r)\bmod a) & \leftarrow f(i-1, j)(g(i-1, j) + q + [j+r\geq a])p_1. \\ \end{aligned} \]

这部分时间复杂度 \(O(na)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N = 1e3+5, D = 11, mod = 998244353;
ll qpow(ll a, int b)
{
    ll r=1;
    for (; b; b>>=1)
    {
        if (b&1) r = r * a % mod;
        a = a * a % mod;
    }
    return r;
}
ll inv(ll a)
{
    return qpow(a, mod-2);
}

struct mat
{
    int n, m;
    ll a[D][D];
    mat(int n = 0, int m = 0, int o = 0) : n(n), m(m)
    {
        memset(a, 0, sizeof(a));
        if (o)
        {
            for (int i=0; i<n; ++i) a[i][i] = 1;
        }
    }
    const ll* operator [] (int i) const {return a[i];}
    ll* operator [] (int i) {return a[i];}
    mat operator * (const mat & b) const
    {
        mat c(n, b.m);
        for (int i=0; i<n; ++i)
            for (int j=0; j<b.m; ++j)
                for (int k=0; k<m; ++k)
                    (c[i][j] += a[i][k] * b[k][j]) %= mod;
        return c;
    }
};

int n, m, A, B, C, w[N];
mat p[N][30];
ll f[N][N], g[N][N];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> A >> B >> C;
    for (int i=1; i<=m; ++i) cin >> w[i];
    for (int i=1; i<=m; ++i)
    {
        p[i][0] = mat(C+1, C+1);
        p[i][0][0][0] = 1;
        for (int j=1; j<=C; ++j)
        {
            ll sum = 0;
            for (int k=0; k<=j; ++k)
                cin >> p[i][0][j][k], sum += p[i][0][j][k];
            sum = inv(sum);
            for (int k=0; k<=j; ++k)
                p[i][0][j][k] = p[i][0][j][k] * sum % mod;
        }
        for (int j=1; j<30; ++j) p[i][j] = p[i][j-1] * p[i][j-1];
    }
    f[0][0] = 1;
    for (int i=1; i<=n; ++i)
    {
        int k; cin >> k;
        mat v(1, C+1, 0); v[0][C] = 1;
        ll sw = 0;
        for (int j=1; j<=k; ++j)
        {
            int id, num; cin >> id >> num;
            for (int l=0; l<30; ++l) if (num >> l & 1)
                v = v * p[id][l];
            sw += 1ll * w[id] * num;
        }
        ll q = (sw / A) % mod;
        int r = sw % A;
        ll p0 = v[0][0], p1 = (1 - p0 + mod) % mod;
        if (i < n)
        {
            for (int j=0; j<A; ++j)
            {
                f[i][r] = (f[i][r] + f[i-1][j] * p0) % mod;
                g[i][r] = (g[i][r] + f[i-1][j] * (g[i-1][j] + q) % mod * p0) % mod;
                f[i][(j+r)%A] = (f[i][(j+r)%A] + f[i-1][j] * p1) % mod;
                g[i][(j+r)%A] = (g[i][(j+r)%A] + f[i-1][j] * (g[i-1][j] + q + (j+r >= A)) % mod * p1) % mod;
            }
            for (int j=0; j<A; ++j) g[i][j] = g[i][j] * inv(f[i][j]) % mod;
            //for (int j=0; j<A; ++j) cout << f[i][j] << ',' << g[i][j] << ' ';
            //cout << '\n';
        }
        else
        {
            ll ans = sw % mod;
            for (int j=0; j<A; ++j)
            {
                ans = (ans + f[i-1][j] * g[i-1][j] % mod * B % mod * p0) % mod;
                ans = (ans + f[i-1][j] * (g[i-1][j] * B % mod + j) % mod * p1) % mod;
            }
            cout << ans << '\n';
        }
    }
}

D. 网络改造

题意

有向图,第 \(i\) 条边可以用 \(a_i\) 的代价反向或用 \(b_i\) 的代价删除,第 \(i\) 个点可以用 \(c_i\) 的代价删除(同时会删除所有与之相连的边),求将图变为 DAG 的最小代价。

\(n\leq 22\)

题解

看范围知状压。

\(f(S)\) 表示将点集 \(S\) 变为 DAG 的最小代价,按拓扑序转移,强制要求下一个点 \(x\) 没有连到 \(S\) 的边,则

\[f(S\cup x)\leftarrow f(S) + \min\{c_x, s(x, S)\} \]

其中 \(s(x, S)\) 表示处理 \(x\)\(S\) 的边的代价和。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

const int N = 22;
int n, m, c[N], f[1<<N], s[N][1<<N];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m;
    for (int i=0; i<n; ++i) cin >> c[i];
    for (int i=1; i<=m; ++i)
    {
        int u, v, a, b;
        cin >> u >> v >> a >> b; --u, --v;
        s[u][1<<v] = min(a, b);
    }
    for (int i=0; i<n; ++i)
        for (int j=0; j<n; ++j)
            for (int S=0; S < (1<<j); ++S)
                s[i][S | (1<<j)] = s[i][S] + s[i][1<<j];
    memset(f, 0x3f, sizeof(f));
    f[0] = 0;
    for (int S=0; S<(1<<n); ++S)
        for (int j=0; j<n; ++j) if (!(S >> j & 1))
            f[S | (1<<j)] = min(f[S | (1<<j)], f[S] + min(c[j], s[j][S]));
    cout << f[(1<<n)-1] << '\n';
}
posted @ 2025-11-12 02:45  EssnSlaryt  阅读(22)  评论(0)    收藏  举报