846A. Curriculum Vitae
题意
给定一个长度为$n$的$01$序列,删掉一些数,使得最终数列中$0$不直接跟在$1$后面,求最长长度。
题解
最终序列一定是$000...111$形式的,$Theta(n)$枚举最后一个$0$的位置,用前缀和$Theta(1)$统计一下即可。
时间复杂度:$Theta(n)$。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 109;
int s[N];
inline int read() {
int s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
inline int count(int l, int r) {
return (r >= l ? s[r] - s[l - 1] : 0);
}
int main() {
int n = read();
for (int i = 1; i <= n; i++) s[i] = read() + s[i - 1];
int res = 0;
for (int i = 0; i <= n; i++) res = max(res, i - count(1, i) + count(i + 1, n));
return 0 * printf("%d
", res);
}
846B. Math Show
题意
给定$n$个大题,每个大题都有$k$个小题,第$i$个小题需要花费$t_i$分钟。每做出一小题得$1$分,每做出完整的一个大题多得一分,即$k+1$分。求在$M$分钟完成的最大得分。
题解
先枚举完整做出的大题数,再按每小题花费时间的贪心即可。
时间复杂度:$Theta(n^2 imes k)$。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 59;
int t[N];
inline int read() {
int s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
int main() {
int n = read(), k = read(), m = read(), s = 0;
for (int i = 1; i <= k; i++) t[i] = read(), s += t[i];
sort(t + 1, t + k + 1);
ll res = 0;
for (int i = 0; i <= n; i++) {
ll tim = i * s, cur = i * (k + 1);
if (tim > m) break;
for (int j = 1; j <= k; j++) {
if (tim + (n - i) * t[j] <= m) tim += (n - i) * t[j], cur += (n - i);
else {
while (tim + t[j] <= m) tim += t[j], cur++;
break;
}
}
res = max(res, cur);
}
return 0 * printf("%d
", res);
}
846C. Four Segments
题意
给定长度为$n$的数列$a_0,a_1,...,a_{n-1}$,定义$sum(l,r)$为区间$[l,r)$内$a_i$的和。求三个数$delim_0,delim_1,delim_2(0leq delim_0leq delim_1leq delim_2
leq n)$将数列分成四段,使得$res=sum(0,delim_0)-sum(delim_0,delim_1)+sum(delim_1,delim_2)-sum(delim_2,n)$最大。
题解
考虑先枚举$delim_1$定下来,再在区间$[0,delim_1]$和$[delim_1,n]$中分别枚举$delim_0$和$delim_2$,这时只要让$sum(0,d_0)$和$sum(d1,d2)$同时取最大,用前缀和加速即可。
时间复杂度:$Theta(n^2)$。
代码
#include <bits/stdc++.h>
#define DE(x) cout << x << endl
using namespace std;
typedef long long ll;
const int N = 5009;
int a[N];
ll s[N];
struct Sol {
int d0, d1, d2;
ll res;
};
inline int read() {
int s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
inline ll sum(int l, int r) {
if (l < 0) return s[r - 1];
return l <= r ? s[r - 1] - s[l - 1] : 0;
}
int main() {
int n = read();
for (int i = 0; i < n; i++) a[i] = read();
s[0] = a[0];
for (int i = 1; i < n; i++) s[i] = s[i - 1] + a[i];
Sol ans; ans.d0 = ans.d1 = ans.d2 = ans.res = 0;
for (int d1 = 0; d1 <= n; d1++) {
int d0 = d1, d2 = d1;
for (int j = 0; j <= d1; j++) if (sum(0, j) > sum(0, d0)) d0 = j;
for (int j = d1; j <= n; j++) if (sum(d1, j) > sum(d1, d2)) d2 = j;
ll res = sum(0, d0) - sum(d0, d1) + sum(d1, d2) - sum(d2, n);
if (res > ans.res) ans.res = res, ans.d0 = d0, ans.d1 = d1, ans.d2 = d2;
}
return 0 * printf("%d %d %d
", ans.d0, ans.d1, ans.d2);
}
846D. Monitor
题意
给定一$n imes m$个的点阵,其中有$q$个点$(x_i,y_i)$会在$t_i$时刻变成坏点。求最少何时,存在一个$k imes k$的点阵全是坏点。
题解
二分时间,单调性显然,用二维前缀和判断是否全是坏点即可。
时间复杂度:$Theta(n^2 imes log q)$。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 509;
int n, m, k, q, x[N*N], y[N*N], t[N*N], s[N][N];
inline int read() {
int s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
inline bool broke(int mid) {
memset(s, 0, sizeof(s));
for (int i = 1; i <= q; i++) if (t[i] <= mid) s[ x[i] ][ y[i] ] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
for (int i = k; i <= n; i++) {
for (int j = k; j <= m; j++) {
if (s[i][j] - s[i - k][j] - s[i][j - k] + s[i - k][j - k] == k * k) return true;
}
}
return false;
}
int main() {
n = read(), m = read(), k = read(), q = read();
for (int i = 1; i <= q; i++) x[i] = read(), y[i] = read(), t[i] = read(), t[0] = max(t[0], t[i]);
int l = -1, r = t[0] + 1, res = -1;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (broke(mid)) r = mid, res = r; else l = mid;
}
return 0 * printf("%d
", res);
}
846E. Chemistry in Berland
题意
给定$n$个物品,现各有$b_i$千克,各需要$a_i$千克。其中$k_i$千克的$x_i$物品可以转化成$1$千克的$i$物品,$1$千克的$i$物品可以转化成$1$千克的$x_i$物品。判断能否通过转化使得$n$种物品都满足需求。
题解
树形$DP$。按着题目所述转化条件转移状态即可。注意数据范围。
时间复杂度:$Theta(n)$。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
const int N = 1e5+9;
ll sum, a[N], b[N], x[N], k[N];
vector<pll> g[N];
bool flag = 1;
inline ll read() {
ll s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
inline void dfs(int u) {
for (auto e : g[u]) {
int k = e.first, v = e.second;
dfs(v);
if (b[v] > 0) b[u] += b[v];
else {
double w = 1.0 * b[v] * k;
if (b[u] + w < -sum) flag = 0;
b[u] += b[v] * k;
}
}
}
int main() {
int n = read();
for (int i = 1; i <= n; i++) b[i] = read(), sum += b[i];
for (int i = 1; i <= n; i++) a[i] = read(), b[i] -= a[i];
for (int i = 2; i <= n; i++) {
int x = read(), k = read();
g[x].push_back(make_pair(k, i));
}
dfs(1);
return 0 * puts(b[1] >= 0 ? "YES" : "NO");
}
846F. Random Query
题意
给定$n$个正整数$a_i$,每次随机地选择两个数$l,r$(若$l>r$,则交换两者),定义$(l,r)$的$value$为$a_l,a_{l+1},...,a_r$中不同的数的个数。求$value$的期望。
题解
我们考虑计算$a_l,a_{l+1},...,a_r$中每个数第一次出现的位置。记$pre[i]$表示$i$之前与$a_i$相等的最近的一个数的位置,则$a_i$能对所有$lin (pre[i],i],rin [i,n]$的所有区间产生$1$个贡献。
故所求期望为$res=frac{2*sum_{i=1}{n}{(i-pre[i])(n-i+1)}-n}{n2}$。
时间复杂度:$Theta(n)$。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+9;
int a[N], pos[N], pre[N];
inline int read() {
int s = 1, a = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
return s * a;
}
int main() {
int n = read();
for (int i = 1; i <= n; i++) a[i] = read(), pre[i] = pos[ a[i] ], pos[ a[i] ] = i;
ll res = 0;
for (int i = 1; i <= n; i++) res += 1ll * (i - pre[i]) * (n - i + 1);
res = res * 2 - n;
return 0 * printf("%.6lf
", 1.0 * res / (1ll * n * n));
}