作为复建还算不错的一场。
AT_abc441_c ABC441C Sake or Water - 洛谷 (luogu.com.cn)
我必须要说一下这个 c,卡了我 20 分钟,排完序才发现前 K 个要从大到小喝。
首先,最坏情况就是所有清酒都在最小容量的 K 个杯子中。
然后,我们按最优方法饮用,因为无论大小容量,喝一杯就只算一杯
肯定喝大杯的划算,这样同样一杯的代价能喝到更多的清酒。
所以方法就是把大的都喝完。
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e5 + 10;
int a[N];
void work() {
int n, K, X;
cin >> n >> K >> X;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
sort (a + 1, a + n + 1);
int sum = 0;
for (int i = K; i >= 1; i --) {
sum += a[i];
if (sum >= X) {
cout << (n - K + K - i + 1) << "\n";
return ;
}
}
cout << "-1\n";
}
signed main () {
ios::sync_with_stdio(false);
cin.tie(0);
work();
return 0;
}
AT_abc441_d ABC441D Paid Walk - 洛谷 (luogu.com.cn)
若只到没话说,随便 dfs 就能过。
AT_abc441_e ABC441E A > B substring - 洛谷 (luogu.com.cn)
也挺简单,前缀和+树状数组。
假设选一段 那么就有要求:
( 数组是从字符串起始到
位置(包括)的 'A' 字母个数累计,
同理)
移项
从小到大遍历 ,以
结尾的段的数量就是
用树状数组即可,常数小,总时间复杂度为 。
注意一下因为 可能为负数,树状数组不能存
的下标。
所以下标统一 + n 就好,我为了保险 + 2 * n。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
LL a[N], b[N];
char s[N];
LL c[N * 4];
int n;
void add(int x, LL d) {
for (int i = x; i <= 4 * n; i += i & -i) {
c[i] += d;
}
}
LL query(int x) {
LL sum = 0;
for (int i = x; i >= 1; i -= i & -i) {
sum += c[i];
}
return sum;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
cin >> s;
a[0] = b[0] = 0;
LL t;
for (int i = 1; i <= n; i ++) {
t = (s[i - 1] == 'A') ? 1 : 0;
a[i] = a[i - 1] + t;
t = (s[i - 1] == 'B') ? 1 : 0;
b[i] = b[i - 1] + t;
}
LL ans = 0;
memset(c, 0, sizeof(c));
add(2 * n, 1);
for (int i = 1; i <= n; i ++) {
t = a[i] - b[i] + n * 2;
ans += query(t - 1);
add(a[i] - b[i] + n * 2, 1);
}
cout << ans << "\n";
return 0;
}
AT_abc441_f ABC441F Must Buy - 洛谷 (luogu.com.cn)
我此前还未听说过前后缀 dp,知道了就很简单。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1005;
const int M = 5e4 + 5;
LL dpa[N][M], dpb[N][M];
int p[N]; LL v[N];
char s[N];
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> p[i] >> v[i];
}
memset(dpa, 0, sizeof(dpa));
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= m; j ++) {
dpa[i][j] = dpa[i - 1][j];
}
for (int j = p[i]; j <= m; j ++) {
dpa[i][j] = max(dpa[i][j], dpa[i - 1][j - p[i]] + v[i]);
}
}
memset(dpb, 0, sizeof(dpb));
for (int i = n; i >= 1; i --) {
for (int j = 0; j <= m; j ++) {
dpb[i][j] = dpb[i + 1][j];
}
for (int j = p[i]; j <= m; j ++) {
dpb[i][j] = max(dpb[i][j], dpb[i + 1][j - p[i]] + v[i]);
}
}
LL mxans = dpa[n][m];
for (int i = 1; i <= n; i ++) {
LL mxno = 0, mxmu = 0;
for (int j = 0; j <= m; j ++) {
mxno = max(mxno, dpa[i - 1][j] + dpb[i + 1][m - j]);
}
for (int j = 0; j <= m - p[i]; j ++) {
mxmu = max(mxmu, v[i] + dpa[i - 1][j] + dpb[i + 1][m - j - p[i]]);
}
if (mxmu < mxans) { // 强制选 < 最优解,那么最优解一定不选
cout << "C";
}
else if (mxno < mxans) { // 不选 < 最优解,那么最优解一定选
cout << "A";
}
else { // 选不选都是最优解,那随便
cout << "B";
}
}
cout << "\n";
return 0;
}
AT_abc441_g ABC441G Takoyaki and Flip - 洛谷 (luogu.com.cn)
典中典懒标记线段树,除了难调以外没难度。
但也是真的很难调。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lc(p) (p << 1)
#define rc(p) ((p << 1) | 1)
const int N = 2e5 + 10;
struct trnode {
int l, r;
LL mxc;
int type; // 区间状态:1全正,2混合,3全反
LL ltg; // 增加 x tag
bool has; // 清空标记
bool flip; // 翻转标记
} tr[N * 4];
void pushup(int p) {
tr[p].mxc = max(tr[lc(p)].mxc, tr[rc(p)].mxc);
if (tr[lc(p)].type == 1 && tr[rc(p)].type == 1) {
tr[p].type = 1;
}
else if (tr[lc(p)].type == 3 && tr[rc(p)].type == 3) {
tr[p].type = 3;
}
else {
tr[p].type = 2;
}
}
void pushdown(int p) {
// 先处理清空标记
if (tr[p].has) {
tr[lc(p)].mxc = 0;
tr[rc(p)].mxc = 0;
tr[lc(p)].ltg = 0;
tr[rc(p)].ltg = 0;
tr[lc(p)].has = true;
tr[rc(p)].has = true;
tr[p].has = false;
}
// 处理翻转标记
if (tr[p].flip) {
// 翻转左孩子
tr[lc(p)].type = 4 - tr[lc(p)].type;
tr[lc(p)].flip ^= 1;
// 翻转右孩子
tr[rc(p)].type = 4 - tr[rc(p)].type;
tr[rc(p)].flip ^= 1;
tr[p].flip = false;
}
// 处理加法标记
if (tr[p].ltg != 0) {
if (tr[lc(p)].type != 3) {
tr[lc(p)].mxc += tr[p].ltg;
tr[lc(p)].ltg += tr[p].ltg;
}
if (tr[rc(p)].type != 3) {
tr[rc(p)].mxc += tr[p].ltg;
tr[rc(p)].ltg += tr[p].ltg;
}
tr[p].ltg = 0;
}
}
void build(int p, int l, int r) {
tr[p] = {l, r, 0, 1, 0, false, false};
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(lc(p), l, mid);
build(rc(p), mid + 1, r);
pushup(p);
}
void update_add(int p, int l, int r, LL x) {
if (r < tr[p].l || tr[p].r < l) {
return;
}
if (l <= tr[p].l && tr[p].r <= r) {
if (tr[p].type != 3) { // 反面朝上的盘子不加章鱼烧
tr[p].mxc += x;
tr[p].ltg += x;
}
return;
}
pushdown(p);
update_add(lc(p), l, r, x);
update_add(rc(p), l, r, x);
pushup(p);
}
void update_flip(int p, int l, int r) {
if (r < tr[p].l || tr[p].r < l) {
return;
}
if (l <= tr[p].l && tr[p].r <= r) {
// 清空所有章鱼烧
tr[p].mxc = 0;
tr[p].ltg = 0;
tr[p].has = true;
// 翻转状态
tr[p].type = 4 - tr[p].type;
tr[p].flip ^= 1;
return;
}
pushdown(p);
update_flip(lc(p), l, r);
update_flip(rc(p), l, r);
pushup(p);
}
LL query(int p, int l, int r) {
if (r < tr[p].l || tr[p].r < l) {
return 0;
}
if (l <= tr[p].l && tr[p].r <= r) {
return tr[p].mxc;
}
pushdown(p);
return max(query(lc(p), l, r), query(rc(p), l, r));
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, Q;
cin >> n >> Q;
build(1, 1, n);
for (int i = 1; i <= Q; i++) {
int opt;
cin >> opt;
if (opt == 1) {
int l, r;
LL x;
cin >> l >> r >> x;
update_add(1, l, r, x);
}
else if (opt == 2) {
int l, r;
cin >> l >> r;
update_flip(1, l, r);
}
else {
int l, r;
cin >> l >> r;
cout << query(1, l, r) << "\n";
}
}
return 0;
}