2021牛客暑期多校训练营4 个人补题记录
比赛链接:Here
1001 - Course
1002 - Sample Game
1003 - LCS
构造,
首先排除不可能的情况
- 两个 LCS 的值减去最小的 LCS 值如果比 \(n\) 还大,那么肯定构造不出来啊(不够长
剩下就是对于公共的 min(a,b,c)
赋 a
,对应的三种情况分别赋予 b,c,d
如果还是不够 \(n\) 则赋予不同值即可
Show Code
int main() { cin.tie(nullptr)->sync_with_stdio(false); int a, b, c, d, n; cin >> a >> b >> c >> n; d = min({a, b, c}); string s1, s2, s3; if (a + c - d > n || a + b - d > n || b + c - d > n) {cout << "NO" << endl; return 0;} for (int i = 1; i <= d; i++) s1 += 'a', s2 += 'a', s3 += 'a'; for (int i = 1; i <= a - d; i++) s1 += 'b', s2 += 'b'; for (int i = 1; i <= b - d; i++) s2 += 'c', s3 += 'c'; for (int i = 1; i <= c - d; i++) s1 += 'd', s3 += 'd'; while (s1.size() < n) s1 += 'e'; while (s2.size() < n) s2 += 'f'; while (s3.size() < n) s3 += 'g'; cout << s1 << endl << s2 << endl << s3 << endl; }
1004 - Rebuild Tree
1005 - Tree Xor
1006 - Just a joke
讨论区有人提出
有两种操作:
- 删除一条边(边的数目-1)
- 删除个连通分量 (点的数目-k,边的数目-(k-1))
所以每次操作边和点的奇偶性都会变化,所以只需要判断边加点和的奇偶性即可。
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
cin >> n >> m;
int x, y;
for (int i = 0; i < m; i++) cin >> x >> y;
if ((n + m) & 1) puts("Alice");
else puts("Bob");
}
在赛时队友想到了用并查集求连通块量 + 单独的点的奇偶性
const int N = 10010;
int f[N], ans;
int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y) f[y] = x;
else ++ans;
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) f[i] = i;
for (int i = 0, x, y; i < m; ++i) {
cin >> x >> y;
merge(x, y);
}
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (f[i] == i) cnt++;
}
cout << ((ans + cnt) % 2 == 0 ? "Bob\n" : "Alice\n");
}
1007 - Product
1008 - Convolution
1009 - Inverse Pair
对原数组处理以后求个逆序对即可
在 \(a\) 数组中选择合适的 \(b_i \in \{0,1\}\) 来最小化逆序对数
求逆序对的方法有树状数组和归并排序,这里写归并排序简单一点
同时一定要开 long long
防止溢出
Show Code
using ll = long long; const int N = 2e5 + 10; ll a[N], tmp[N], n; int vis[N]; ll ans = 0; void Merge(int l, int m, int r) { int i = l; int j = m + 1; int k = l; while (i <= m && j <= r) { if (a[i] > a[j]) { tmp[k++] = a[j++]; ans += m - i + 1; } else tmp[k++] = a[i++]; } while (i <= m) tmp[k++] = a[i++]; while (j <= r) tmp[k++] = a[j++]; for (int i = l; i <= r; i++) a[i] = tmp[i]; } void M_sort(int l, int r) { if (l < r) { int m = (l + r) >> 1; M_sort(l, m); M_sort(m + 1, r); Merge(l, m, r); } } int main() { cin.tie(nullptr)->sync_with_stdio(false); cin >> n; for (int i = 1; i <= n; ++i) { cin >> a[i]; if (vis[a[i] + 1] == 1) { a[i] += 1, vis[a[i] + 1] = 2; } else vis[a[i]] = 1; } // for (int i = 1; i <= n; ++i) cout << a[i] << " \n"[i == n]; M_sort(1, n); cout << ans << "\n"; }
1010 - Average
为什么连板子都没看出来啊?明明比赛前一天还复习了单调队列优化DP的题
由 \(a_{1...n}\) 和 \(b_{i...m}\) 构成的数组形成的 \(n\times m\) 的矩阵求至少长度为 \(x\times y\) 的最大值子矩阵
我们要求至少长度为 \(x\times y\) 的最大值子矩阵,转化过来就是求出 \(a_{1...n}\) 和 \(b_{i...m}\) 中找一个最长的连续一段区间使其平均值最大。最后两者相加即可
二分答案平均值。
judge时把每一个a[i]-mid得到b[i]
在b[i]中找到一段合法的串使其权值和最大。
当最大权值和大于等于0时则mid上移。
求最大权值和用单调队列就行。(预处理b[i]的前缀和sum[i],队列中记录当前位置可选区间的最小的sum[i])
核心代码参考:Here
bool check(double x) {
int i, l = 1, r = 0;
for (i = 1; i <= n; i++)
a[i] = (double)b[i] - x;
sum[0] = 0;
for (i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
for (i = 1; i <= n; i++) {
if (i >= s) {
while (r >= l && sum[i - s] < sum[q[r]])r--;
q[++r] = i - s;
}
if (l <= r && q[l] < i - t)l++;
if (l <= r && sum[i] - sum[q[l]] >= 0)return true;
}
return false;
}
int main() {
...
int ans = l = -10000; r = 10000;
while (r - l > 1e-5) {
mid = (l + r) / 2;
if (check(mid)) ans = l = mid;
else r = mid;
}
printf("%.3lf\n", ans);
return 0;
}
【AC Code】
Show Code
const int N = 1e5 + 10; int pi[N]; double pp[N]; double solve(int k, int a) { double l = 0.0, r = 100001.0; for (int i = 1; i <= k; ++i) cin >> pi[i]; while (r - l > 1e-8) { double m = (l + r) / 2.0, p = 0.0, sum = 0; for (int i = 1; i <= a; ++i) pp[i] = pp[i - 1] + pi[i] - m; sum = pp[a]; for (int i = a + 1; i <= k; ++i) { p = min(p, pp[i - a]); pp[i] = pp[i - 1] + pi[i] - m; sum = max(sum, pp[i] - p); } if (sum >= 0) l = m; else r = m; } return r; } int main() { cin.tie(nullptr)->sync_with_stdio(false); int n, m, a, b; cin >> n >> m >> a >> b; cout << fixed << setprecision(10) << (solve(n, a) + solve(m, b)); }