2025.12.7 百度之星决赛 2025

Solved:6/12

  • B. 0:45
  • H. 1:35(-2)
  • E. 2:00(-3)
  • G. 2:45(-2)
  • J. 3:50(-4)
  • C. 4:30(-1)

Rank:33(大学组)/ 122(全部)


E.

题意

棋盘有 \(L\) 个格子,从左到右编号为 \(1,2,\dots, L\)。初始 \(n\) 个棋子位于 \(1,2,\dots,n\) 号格子,每次可以将任意一个棋子移动到右侧任意一个空格子,需保证:假设当前最左侧的棋子在第 \(x\) 格,则任意一个棋子不能超过第 \(x+d\) 格。若某次移动后棋子移动到第 \(L\) 个格子则结束。求最少和最多的移动次数。

\(1\leq n\leq d\leq L\leq 10^9, n<L\)

题解

最多:从右到左每个棋子移动一格作为一次循环,每次循环 \(n\) 步整体移动 \(1\) 格,循环到第 \(L-n\) 轮的第一步结束。

最少:从左到右每个棋子移动 \(d\) 格作为一次循环,每次循环 \(n\) 步整体移动 \(d\) 格,最后可能有一个不完整的循环。

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

void solve()
{
    ll n, d, l, ans; cin >> n >> d >> l;
    ll t = (l-n) / d;
    ans = t * n + (l == t * d + n ? 0ll : max(1ll, l - (t+1)*d));
    cout << ans << ' ' << (l-n-1) * n + 1 << '\n';
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int T; cin >> T;
    while (T--) solve();
}

H.

题意

\(X = f_{n-1}(f_{n-2}(\dots f_2(f_1(a_1,a_2),a_3),\dots),a_n)\),其中 \(a_1,\dots,a_n\) 已知,\(f_i\)\(p\) 的概率取 \(\min\)\(1-p\) 的概率取 \(\max\)。求 \(\mathbb E[X]\)。对 \(10^9+7\) 取模。

\(1\leq n\leq 2\times 10^5\)

题解

离散化 \(a\),然后维护 \(X\) 取每个值的概率。

每套一层 \(f\),小于 \(a_i\) 的值概率乘 \(p\),大于 \(a_i\) 的值概率乘 \(1-p\),等于 \(a_i\) 的值概率可直接计算。

区间乘 / 区间求和。

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

const int N = 2e5+5, mod = 1e9+7;
ll qpow(ll x, int y)
{
    ll r = 1;
    for (; y; y >>= 1)
    {
        if (y&1) r = r * x % mod;
        x = x * x % mod;
    }
    return r;
}
ll inv(ll x)
{
    return qpow(x, mod-2);
}

int n, m, a[N], buc[N];
ll p, q;

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)

ll sum[N*4], tag[N*4];
void pu(int x)
{
    sum[x] = (sum[lc] + sum[rc]) % mod;
}
void mul(int x, ll val)
{
    sum[x] = sum[x] * val % mod;
    tag[x] = tag[x] * val % mod;
}
void pd(int x)
{
    mul(lc, tag[x]), mul(rc, tag[x]), tag[x] = 1;
}
void upd(int x, int l, int r, int p, ll val)
{
    if (l == r)
    {
        sum[x] = (sum[x] + val) % mod;
        return;
    }
    pd(x);
    if (p <= mid) upd(lc, l, mid, p, val);
    else upd(rc, mid+1, r, p, val);
    pu(x);
}
void upd(int x, int l, int r, int L, int R, ll val)
{
    if (L > R) return;
    if (l == L && r == R)
    {
        mul(x, val);
        return;
    }
    pd(x);
    if (R <= mid) upd(lc, l, mid, L, R, val);
    else if (L > mid) upd(rc, mid+1, r, L, R, val);
    else upd(lc, l, mid, L, mid, val), upd(rc, mid+1, r, mid+1, R, val);
    pu(x);
}
ll qry(int x, int l, int r, int L, int R)
{
    if (L > R) return 0;
    if (l == L && r == R) return sum[x];
    pd(x);
    if (R <= mid) return qry(lc, l, mid, L, R);
    if (L > mid) return qry(rc, mid+1, r, L, R);
    return (qry(lc, l, mid, L, mid) + qry(rc, mid+1, r, mid+1, R)) % mod;
}
ll ans = 0;
void trv(int x, int l, int r)
{
    if (l == r)
    {
        ans = (ans + buc[l] * sum[x]) % mod;
        return;
    }
    pd(x);
    trv(lc, l, mid), trv(rc, mid+1, r);
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> p;
    for (int i=1; i<=n; ++i) cin >> a[i], buc[i] = a[i];
    sort(buc+1, buc+n+1); m = unique(buc+1, buc+n+1) - buc - 1;
    for (int i=1; i<=n; ++i) a[i] = lower_bound(buc+1, buc+m+1, a[i]) - buc;
    p = inv(100) * p % mod; q = (mod + 1 - p) % mod;
    for (int i=1; i<=4*m; ++i) tag[i] = 1;
    upd(1, 1, m, a[1], 1);
    for (int i=2; i<=n; ++i)
    {
        ll np = (q * qry(1, 1, m, 1, a[i]) + p * qry(1, 1, m, a[i]+1, m)) % mod;
        upd(1, 1, m, 1, a[i], p);
        upd(1, 1, m, a[i]+1, m, q);
        upd(1, 1, m, a[i], np);
    }
    trv(1, 1, m);
    cout << ans << '\n';
}

C.

题意

给两个长为 \(n\) 的序列 \(a,b\),定义排列 \(p\) 的价值是满足 \(a_{p_x} = \sum_{i=1}^{x-1} b_{p_i}\)\(x\) 的数量,求所有 \(n\) 阶全排列的价值之和。对 \(10^9+7\) 取模。

\(1\leq n\leq 200, 1\leq a_i\leq 10^9, 1\leq b_i\leq 100\)

题解

分别算每个位置的贡献。每存在一组 \(S\subset \{1,2,\dots,n\}\backslash \{x\}\) 使得 \(a_x = \sum_{i\in S} b_i\),就会对答案贡献 \(k!(n-k-1)!\)。其中 \(k = |S|\)

因此只需计算关于 \(b\) 的去掉一个物品的背包方案数。额外开一维状态表示物品个数。

去掉一个物品可以容斥。

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

const int N = 205, M = 2e4 + 5, mod = 1e9 + 7;
ll fac[N];
void init(int n)
{
    fac[0] = 1;
    for (int i=1; i<=n; ++i) fac[i] = fac[i-1] * i % mod;
}

int n, m, a[N], b[N];
ll f[N][M], ans;
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n; init(n);
    for (int i=1; i<=n; ++i) cin >> a[i];
    for (int i=1; i<=n; ++i) cin >> b[i], m += b[i];
    f[0][0] = 1;
    for (int k=1; k<=n; ++k)
        for (int i=n; i; --i)
            for (int j=b[k]; j<=m; ++j)
                f[i][j] = (f[i][j] + f[i-1][j-b[k]]) % mod;
    for (int k=1; k<=n; ++k)
    {
        if (a[k] > m) continue;
        for (int i=1; i<=n-1; ++i)
        {
            ll val = 0;
            for (int l=0, o=1; l <= i && b[k]*l <= a[k]; ++l, o = -o)
                val += o * f[i-l][a[k] - l*b[k]];
            val = (val % mod + mod) % mod;
            ans = (ans + fac[i] * fac[n-i-1] % mod * val) % mod;
        }
    }
    cout << ans << '\n';
}

B.

题意

\(n\) 种插头和对应的 \(n\) 种插座,有 \(K\) 个用电器,第 \(i\) 个用电器的插头种类是 \(a_i\);有 \(L\) 个插座,第 \(i\) 个插座的插座种类是 \(b_i\)

另外有 \(m\) 种转接器,第 \(i\) 种转接器有 \(c_i\) 个。第 \(i\) 种转接器可以将种类为 \(p_{i,1},\dots,p_{i,r_i}\) 的插头和种类为 \(q_{i,1},\dots,q_{i,s_i}\) 的插座相连。可以串连多个转接器。

求连接所有用电所需要使用的转接器数量的最小值。

\(1\leq n,K,L,m\leq 100\)

题解

费用流板子。

建 4 排点,源 → 插头 → 转接器in → 转接器out → 插座 → 汇。然后按要求建图即可。

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

const int V = 505, E = 1<<18, inf = 1e9;
int S, T, tote=1, fir[V], to[E], nxt[E], w[E], c[E];
void adde(int x, int y, int z, int t)
{
    to[++tote] = y, nxt[tote] = fir[x], fir[x] = tote, w[tote] = z, c[tote] = t;
    to[++tote] = x, nxt[tote] = fir[y], fir[y] = tote, w[tote] = 0, c[tote] = -t;
}
int dis[V], q[E];
bool vis[V];
bool bfs()
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    queue <int> q;
    q.push(T); dis[T] = 0;
    while (!q.empty())
    {
        int u = q.front(); q.pop(); vis[u] = 0;
        for (int i = fir[u]; i; i = nxt[i])
        {
            int v = to[i];
            if (!w[i^1] || dis[v] <= dis[u] + c[i^1]) continue;
            dis[v] = dis[u] + c[i^1];
            if (!vis[v]) vis[v] = 1, q.push(v);
        }
    }
    return dis[S] < inf;
}
int cur[V];
int dfs(int u, int flow)
{
    if (u == T || !flow) return flow;
    vis[u] = 1;
    int nowf = flow;
    for (int & i = cur[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (dis[v] + c[i] != dis[u] || vis[v]) continue;
        int f = dfs(v, min(w[i], nowf));
        w[i] -= f; w[i^1] += f;
        if (!(nowf -= f)) return flow;
    }
    return flow - nowf;
}
pii MCMF()
{
    int flow = 0, res = 0;
    while (bfs())
    {
        memcpy(cur, fir, sizeof(cur));
        memset(vis, 0, sizeof(vis));
        int f = dfs(S, inf);
        flow += f, res += dis[S] * f;
    }
    return pii(flow, res);
}

const int N = 105;
int n, m, k, l, x;
int id=0, a[N], b[N], u[N], v[N];
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> k >> l;
    S = ++id; T = ++id;
    for (int i=1; i<=n; ++i) a[i] = ++id;
    for (int i=1; i<=n; ++i) b[i] = ++id;
    for (int i=1; i<=m; ++i) u[i] = ++id;
    for (int i=1; i<=m; ++i) v[i] = ++id;
    for (int i=1; i<=n; ++i) adde(a[i], b[i], inf, 0);
    for (int i=1; i<=k; ++i) cin >> x, adde(S, a[x], 1, 0);
    for (int i=1; i<=l; ++i) cin >> x, adde(b[x], T, 1, 0);
    for (int i=1; i<=m; ++i)
    {
        int t, p, q; cin >> t >> p >> q;
        adde(u[i], v[i], t, 1);
        for (int j=1; j<=p; ++j) cin >> x, adde(a[x], u[i], inf, 0);
        for (int j=1; j<=q; ++j) cin >> x, adde(v[i], a[x], inf, 0);
    }
    pii ans = MCMF();
    if (ans.first < k) cout << "-1\n";
    else cout << ans.second << '\n';
}

J.

题意

\(n\) 个商店和 \(m\) 件商品。第 \(i\) 个商店里第 \(j\) 件商品的售价为 \(a_{i,j}\)

\(n\) 个商店在 \(n+1\) 个点的无向带权图中,起点是 \(n+1\) 号点。

你需要选择一部分商店(假设为 \(S\)),代价是 \(f(S) + \sum_{i=1}^m \min_{j\in S} a_{i,j}\)。其中 \(f(S)\) 是从起点出发以任意顺序经过 \(S\) 中的点再回到起点的路径边权和的最小值。求最小代价。

\(1\leq n\leq 16, 1\leq m\leq 5000\)

题解

二合一。

\(f\) 的部分就是旅行商问题。状压dp。

另一部分需要 \(O(2^n)\) 求出每个固定的 \(i\) 对应的所有 \(S\)\(\min_{j\in S} a_{i,j}\)。因为 \(2^n\times m \approx 3.3\times 10^8\) 已经几乎是 1s 能跑的上限,所以这部分常数要尽可能小,直接递推可能会 TLE。赛时将 \(2^{16}\) 的递推数组拆成两个 \(2^8\) 的之后在统计答案时合并,减少大跨度的内存访问,1600ms→700ms。

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

const int N = 16, M = 5005;
int n, m, k, a[M][N], w1[256], w2[256];
ll d[N+1][N+1], f[1<<N][N], g[1<<N];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >> m >> k;
    for (int i=0; i<n; ++i)
        for (int j=1; j<=m; ++j)
            cin >> a[j][i];
    memset(d, 0x3f, sizeof(d));
    for (int i=1; i<=k; ++i)
    {
        int x, y, z; cin >> x >> y >> z;
        --x, --y, d[x][y] = d[y][x] = z;
    }
    for (int k=0; k<=n; ++k)
        for (int i=0; i<=n; ++i)
            for (int j=0; j<=n; ++j)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    memset(f, 0x3f, sizeof(f));
    memset(g, 0x3f, sizeof(g));
    for (int i=0; i<n; ++i) f[1<<i][i] = d[n][i];
    for (int S=1; S<1<<n; ++S)
        for (int i=0; i<n; ++i) if (S >> i & 1)
            for (int j=0; j<n; ++j) if (!(S >> j & 1))
                f[S | 1<<j][j] = min(f[S | 1<<j][j], f[S][i] + d[i][j]);
    for (int S=1; S<1<<n; ++S)
        for (int i=0; i<n; ++i) if (S >> i & 1)
            g[S] = min(g[S], f[S][i] + d[i][n]);
    for (int i=1; i<=m; ++i)
    {
        w1[0] = w2[0] = 2e9;
        for (int S=1; S<256; ++S)
            w1[S] = min(w1[S ^ (S & -S)], a[i][__lg(S & -S)]);
        if (n > 8)
            for (int S=1; S<1<<n-8; ++S)
                w2[S] = min(w2[S ^ (S & -S)], a[i][__lg(S & -S) + 8]);
        for (int S=1; S<1<<n; ++S)
            g[S] += min(w1[S&255], w2[S>>8]);
    }
    ll ans = 1e18;
    for (int S=1; S<1<<n; ++S) ans = min(ans, g[S]);
    cout << ans << '\n';
}

G.

题意

一个箱子在二维平面上滑动,初始位于 \((0,0)\)。平面上有 \(n\) 个障碍,箱子每次滑动会沿水平或竖直方向移动直到遇到障碍,箱子停留的格子(即遇到障碍之前的格子)会被标记。特别地 \((0,0)\) 初始已被标记。求被标记的格子数量的最大值。

\(1\leq n\leq 10^5, -10^9\leq x_i, y_i\leq 10^9\)

题解

可标记的格子只有箱子周围的四个位置。用 set 和 map 维护这些位置并建图,然后缩点+DAG最长路。

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

const int N = 4e5 + 5;
int n, m, x, y;
map <int, set <int>> R, C;
map <pii, int> id;
vector <int> e[N], g[N];
void adde(vector<int>* e, int x, int y)
{
    e[x].push_back(y);
}

int dfi = 0, dfn[N], low[N], sta[N], top;
bool in[N];
int cnt = 0, bel[N], siz[N];
void dfs(int u)
{
    dfn[u] = low[u] = ++dfi;
    sta[++top] = u, in[u] = 1;
    for (int v : e[u])
    {
        if (!dfn[v]) dfs(v), low[u] = min(low[u], low[v]);
        else if (in[v]) low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u])
    {
        ++cnt;
        int v;
        do
        {
            v = sta[top--];
            bel[v] = cnt;
            ++siz[cnt];
            in[v] = 0;
        } while (v != u);
    }
}

int f[N], d[N];
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    id[pii(0, 0)] = ++m;
    cin >> n;
    for (int i=1; i<=n; ++i)
    {
        cin >> x >> y;
        if (!id.count(pii(x+1, y))) id[pii(x+1, y)] = ++m;
        if (!id.count(pii(x-1, y))) id[pii(x-1, y)] = ++m;
        if (!id.count(pii(x, y+1))) id[pii(x, y+1)] = ++m;
        if (!id.count(pii(x, y-1))) id[pii(x, y-1)] = ++m;
        R[y].insert(x);
        C[x].insert(y);
    }
    for (auto & [p,i] : id)
    {
        auto [x,y] = p;
        {
            auto & s = R[y];
            auto it = s.lower_bound(x);
            if (it != s.end() && *it - 1 != x) adde(e, i, id[pii(*it - 1, y)]);
            if (it != s.begin())
            {
                --it;
                if (*it + 1 != x) adde(e, i, id[pii(*it + 1, y)]);
            }
        }
        {
            auto & s = C[x];
            auto it = s.lower_bound(y);
            if (it != s.end() && *it - 1 != y) adde(e, i, id[pii(x, *it - 1)]);
            if (it != s.begin())
            {
                --it;
                if (*it + 1 != y) adde(e, i, id[pii(x, *it + 1)]);
            }
        }
    }
    for (int i=1; i<=m; ++i) if (!dfn[i]) dfs(i);
    for (int i=1; i<=m; ++i)
        for (int j : e[i])
            if (bel[j] != bel[i])
                adde(g, bel[i], bel[j]), d[bel[j]]++;
    queue <int> q;
    memset(f, 0xcf, sizeof(f));
    f[bel[1]] = siz[bel[1]];
    for (int i=1; i<=cnt; ++i) if (!d[i]) q.push(i);
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        for (int v : g[u])
        {
            f[v] = max(f[v], f[u] + siz[v]);
            if (!--d[v]) q.push(v);
        }
    }
    int ans = 0;
    for (int i=1; i<=cnt; ++i) ans = max(ans, f[i]);
    cout << ans << '\n';
}
posted @ 2025-12-08 22:52  EssnSlaryt  阅读(1)  评论(0)    收藏  举报