正睿 25 年联赛联合训练 Day 10
正睿 25 年联赛联合训练 Day 10
得分
| T1 | T2 | T3 | T4 | 总分 | 排名 |
|---|---|---|---|---|---|
| \(80\) | \(100\) | \(70\) | \(0\) | \(250\) | \(4/35\) |
- T1 忘了不能操作 \(1,n\),\(100\to 80\)。
题解
T1 最大最小
简单题,显然考虑二分答案,有超出的部分就按下去,看能不能按完即可。复杂度 \(O(n\log V)\)。
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
int n, a[Maxn];
int b[Maxn];
bool check(int x) {
for(int i = 1; i <= n; i++) b[i] = a[i];
for(int i = 2; i < n; i++) {
if(b[i] > x) {
if(b[i] > b[i + 1] && b[i] > b[i - 1]) {
int mx = max(b[i - 1], b[i + 1]);
int num = min((b[i] - mx) >> 1, b[i] - x);
b[i] -= num, b[i - 1] += num, b[i + 1] += num;
}
}
}
for(int i = 1; i <= n; i++) if(b[i] > x) return 0;
return 1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int l = 0, r = 1e9, ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
cout << ans << '\n';
return 0;
}
T2 魔法树
考虑一个关键性质:每个点出现的间隔点数一定是一样的,并且等于它到根上每个点的儿子个数的乘积。于是我们就可以考虑递归的思路:先判断间隔点数不一样的情况,然后对所有出现两次以上的点求出它的间隔点数的 \(\gcd\),那么当前节点的儿子个数就是这个 \(\gcd\)。
如果这个值是 \(1\) 那么显然不可行,返回不合法,否则继续向下递归;我们已经知道了当前根的儿子个数,那么就可以知道每个子树内数字的出现顺序,递归求解子问题即可。每次递归节点处至少减半,复杂度 \(O(m\log m)\)。
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
int T;
int n, a[Maxn];
int vis[Maxn], pos[Maxn], len[Maxn];
bool dfs(vector <int> &v) {
bool flg = 1, sb = 0;
int g = 0, cnt = 0;
for(int i = 0; i < v.size(); i++) vis[v[i]] = pos[v[i]] = len[v[i]] = 0;
for(int i = 0; i < v.size(); i++) {
if(!vis[v[i]]) {
vis[v[i]] = 1, pos[v[i]] = i;
cnt++;
}
else {
flg = 0; int num = i - pos[v[i]];
pos[v[i]] = i;
if(len[v[i]] == 0) len[v[i]] = num, g = __gcd(g, num);
else {
if(len[v[i]] != num) sb = 1;
}
}
}
if(flg || cnt == 1) return 1;
if(sb || g == 1) return 0;
vector <int> nxt[g];
for(int i = 0; i < v.size(); i++) nxt[i % g].push_back(v[i]);
vector<int>().swap(v);
bool res = 1;
for(int i = 0; i < g; i++) res &= dfs(nxt[i]);
return res;
}
void solve() {
cin >> n;
vector <int> v(n);
for(int i = 1; i <= n; i++) cin >> v[i - 1];
bool ans = dfs(v);
if(ans) cout << "Yes\n";
else cout << "No\n";
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T;
while(T--) solve();
return 0;
}
T3 合并石子
这个第 \(k\) 大的期望非常的熟悉啊,我们直接上一个 Min-Max 容斥搞一搞:
那么重点就在于求出所有子集大小为 \(|T|\) 的子集的最小值期望数。这个我们继续转化成所有大小为 \(|T|\) 的最小值和除以 \(\binom{n}{|T|}\)。现在的问题就是求 \(|T|\) 个数的最小值之和了。
我们先转化成最小值 \(x\) 乘上最小值为 \(x\) 的子集数量,考虑怎样求后者。我们再用一个经典容斥套路,转化为求出最小值 \(\ge x\) 的子集数量。此时我们发现,每个 \(A_i\) 匹配的 \(B_i\) 要满足 \(A_i+B_i\ge x\),排序后可求出每个 \(A_i\) 对应的可选的 \(B_i\) 是一段前缀,然后直接 dp,设 \(dp(i,j)\) 表示前 \(i\) 个数选出了 \(j\) 个,满足最小值 \(\ge x\) 的方案数。转移方程为:
实践中可以滚动数组,然后我们就可以求出答案了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
const int Mod = 1e9 + 7;
int n, k, a[Maxn], b[Maxn];
int qpow(int a, int b) {
int res = 1;
while(b) {
if(b & 1) res = res * a % Mod;
a = a * a % Mod, b >>= 1;
}
return res;
}
int f[Maxn], g[Maxn];
void init() {
f[0] = 1;
for(int i = 1; i <= n; i++) f[i] = f[i - 1] * i % Mod;
g[n] = qpow(f[n], Mod - 2);
for(int i = n - 1; i >= 0; i--) g[i] = g[i + 1] * (i + 1) % Mod;
}
int C(int n, int m) {
if(n < m) return 0;
return f[n] * g[m] % Mod * g[n - m] % Mod;
}
int A(int n, int m) {
if(n < m) return 0;
return f[n] * g[n - m] % Mod;
}
int pos[Maxn], dp[Maxn], sum[405][805];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> k;
init();
int mx1 = 0, mx2 = 0;
for(int i = 1; i <= n; i++) {
cin >> a[i]; mx1 = max(mx1, a[i]);
}
for(int i = 1; i <= n; i++) {
cin >> b[i]; mx2 = max(mx2, b[i]);
}
int ans = 0;
sort(a + 1, a + n + 1, [](int x, int y){return x < y;});
sort(b + 1, b + n + 1, [](int x, int y){return x > y;});
for(int mn = 1; mn <= mx1 + mx2; mn++) {
for(int i = 1; i <= n; i++) {
pos[i] = upper_bound(b + 1, b + n + 1, mn - a[i], greater<int>()) - b - 1;
dp[i] = 0;
}
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = min(pos[i], i - 1); j >= 0; j--) {
int num = pos[i] - j;
dp[j + 1] = (dp[j + 1] + dp[j] * num % Mod) % Mod;
}
}
for(int i = 1; i <= n; i++) {
sum[i][mn] = (sum[i][mn] + dp[i] % Mod) % Mod;
}
}
for(int i = k; i <= n; i++) {
int num = 0;
for(int j = 1; j <= mx1 + mx2; j++) {
num = (num + (sum[i][j] - sum[i][j + 1] + Mod) % Mod * j) % Mod;
}
int res = C(i - 1, k - 1) * num % Mod * qpow(A(n, i), Mod - 2) % Mod;
if((i - k) & 1) ans = (ans - res + Mod) % Mod;
else ans = (ans + res) % Mod;
}
cout << ans << '\n';
return 0;
}
T4 联通格子
把所有点离散化,然后这些点形成了一个网格图。此时点数为 \(196\),关键点数为 \(14\)。我们直接跑斯坦纳树加上一些常数优化就可以直接跑过了。由于斯坦纳树求出来的是边数,而要求的是点数,所以加一即可。
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 2e5 + 5;
const int Inf = 2e9;
int n, m, x[Maxn], y[Maxn];
int tx[Maxn], tot1, ty[Maxn], tot2;
int head[Maxn], edgenum;
struct node {
int nxt, to, w;
}edge[Maxn];
void add(int u, int v, int w) {
edge[++edgenum] = {head[u], v, w}; head[u] = edgenum;
edge[++edgenum] = {head[v], u, w}; head[v] = edgenum;
}
int mat(int i, int j) {return (i - 1) * tot2 + j;}
int dp[1 << 14][200];
queue <int> q;
bool vis[Maxn];
void SPFA(int S) {
for(int i = 1; i <= m; i++) if(dp[S][i] != Inf) q.push(i), vis[i] = 1;
while(!q.empty()) {
int x = q.front(); q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = edge[i].nxt) {
int to = edge[i].to;
if(dp[S][to] > dp[S][x] + edge[i].w) {
dp[S][to] = dp[S][x] + edge[i].w;
if(!vis[to]) q.push(to), vis[to] = 1;
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
tx[++tot1] = x[i], ty[++tot2] = y[i];
}
sort(tx + 1, tx + tot1 + 1), sort(ty + 1, ty + tot2 + 1);
tot1 = unique(tx + 1, tx + tot1 + 1) - tx - 1;
tot2 = unique(ty + 1, ty + tot2 + 1) - ty - 1;
for(int i = 1; i <= tot1; i++) {
for(int j = 1; j <= tot2; j++) {
if(j != tot2) add(mat(i, j), mat(i, j + 1), ty[j + 1] - ty[j]);
if(i != tot1) add(mat(i, j), mat(i + 1, j), tx[i + 1] - tx[i]);
}
}
m = tot1 * tot2;
for(int i = 0; i < (1 << n); i++) for(int j = 1; j <= m; j++) dp[i][j] = Inf;
for(int i = 1; i <= n; i++) {
int px = lower_bound(tx + 1, tx + tot1 + 1, x[i]) - tx;
int py = lower_bound(ty + 1, ty + tot2 + 1, y[i]) - ty;
dp[1 << (i - 1)][mat(px, py)] = 0;
}
for(int S = 1; S < (1 << n); S++) {
for(int T = S & (S - 1); T; T = (T - 1) & S) {
if(T < (S ^ T)) break;
for(int i = 1; i <= m; i++) dp[S][i] = min(dp[S][i], dp[T][i] + dp[T ^ S][i]);
}
SPFA(S);
}
int ans = Inf;
for(int i = 1; i <= m; i++) ans = min(ans, dp[(1 << n) - 1][i]);
cout << ans + 1 << '\n';
return 0;
}

浙公网安备 33010602011771号