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 s