ABC round 426
- T3
题意略,大概就是写一棵支持单点加和区间修改的线段树。
敲就完事了。
#include <bits/stdc++.h>
#define int long long
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define rep_(i, a, b) for(int i = a; i >= b; --i)
using namespace std;
int n, m, x, y, now;
constexpr int N = 1e6 + 6;
struct Seg {
int sum[N << 2], change[N << 2];
inline void pushup(int i) {
sum[i] = sum[i << 1] + sum[i << 1 | 1];
}
inline void pushdown(int i) {
if(change[i] != -1) {
sum[i << 1] = sum[i << 1 | 1] = 0;
change[i << 1] = change[i << 1 | 1] = 0;
change[i] = -1;
}
}
void build(int i, int l, int r) {
change[i] = -1;
if(l == r) {
sum[i] = 1;
return;
}
int mid = l + r >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
pushup(i);
}
inline int query(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) {
return sum[i];
}
pushdown(i);
int mid = l + r >> 1, ans = 0;
if(L <= mid) {
ans += query(i << 1, l, mid, L, R);
}
if(R > mid) {
ans += query(i << 1 | 1, mid + 1, r, L, R);
}
return ans;
}
void modify(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) {
sum[i] = change[i] = 0;
return;
}
pushdown(i);
int mid = l + r >> 1;
if(L <= mid) {
modify(i << 1, l, mid, L, R);
}
if(R > mid) {
modify(i << 1 | 1, mid + 1, r, L, R);
}
pushup(i);
}
void update(int i, int l, int r, int pos, int k) {
if(l == r) {
sum[i] += k;
return;
}
pushdown(i);
int mid = l + r >> 1;
if(pos <= mid) {
update(i << 1, l, mid, pos, k);
}
else {
update(i << 1 | 1, mid + 1, r, pos, k);
}
pushup(i);
}
}T;
signed main() {
cin >> n >> m;
T.build(1, 1, n);
rep(i, 1, m) {
cin >> x >> y;
now = T.query(1, 1, n, 1, x);
cout << now << '\n';
T.update(1, 1, n, y, now);
T.modify(1, 1, n, 1, x);
}
return 0;
}
- T4
你先选定一种最后想让这个序列变成的数(0 或者 1)。我下面以选定 0 为例子。
容易想到要得到一个自由移动的 0 一定需要 2 的代价,得到一个自由移动的 1 需要 1 的代价。
然后发现可以有一个连续 0 子串不动,那把这个子串求出来之后加代价就行。
呃选定 1 是同理的,两者取 \(\max\) 即可。
#include <bits/stdc++.h>
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
using namespace std;
constexpr int N = 5e5 + 5;
int t, n, sum0, sum1, mx0, mx1, now0, now1;
char a[N];
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
cin >> t;
while(t--) {
sum0 = sum1 = mx0 = mx1 = now0 = now1 = 0;
cin >> n;
rep(i, 1, n) {
cin >> a[i];
if(a[i] == '1') {
sum1++;
now1++;
now0 ^= now0;
}
else if(a[i] == '0') {
sum0++;
now0++;
now1 ^= now1;
}
mx0 = max(mx0, now0);
mx1 = max(mx1, now1);
}
cout << min(2 * (sum0 - mx0) + sum1, 2 * (sum1 - mx1) + sum0) << '\n';
}
return 0;
}
- T5
没做。
- T6
维护一棵支持区间加和区间最小值,还有区间内还没被买完货物的种类数的线段树。
对于每一个询问,先循环找这个区间里所有小于等于 \(k\) 的数,然后通过线段树二分一个一个给修改成一个极大值(因为这东西后续就不会再用了),顺便返回它们的值,把这些贡献加起来得到一个 \(a\)。
然后其他大于 \(k\) 的数直接得到一个贡献和是 \(k \times num\) (num指还没被买完的货物的种类数)
两个加一下就好了。
由于一种货物只会被修改一次,而 \(n\), \(m\) 同阶,所以复杂度应该是 \(O(n\log n)\) 的。
#include <bits/stdc++.h>
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
using namespace std;
typedef long long ll;
constexpr int N = 3e5 + 5;
ll b, c, d, n, m, a[N];
struct Seg {
ll Min[N << 2], add[N << 2], num[N << 2];
#define ls (i << 1)
#define rs (i << 1 | 1)
void pushup(int i) {
Min[i] = min(Min[ls], Min[rs]);
num[i] = num[ls] + num[rs];
}
inline void pushdown(int i) {
if(add[i]) {
add[ls] += add[i];
add[rs] += add[i];
Min[ls] += add[i];
Min[rs] += add[i];
add[i] ^= add[i];
}
}
void build(int i, int l, int r) {
if(l == r) {
Min[i] = a[l];
num[i] = 1;
return;
}
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(i);
}
ll update(int i, int l ,int r, ll k) {
ll res = 0;
if(l == r) {
res = Min[i];
Min[i] = (ll)1e18;
num[i] = 0;
return res;
}
pushdown(i);
int mid = l + r >> 1;
if(Min[ls] <= k) res = update(ls, l, mid, k);
else res = update(rs, mid + 1, r, k);
pushup(i);
return res;
}
ll query(int i, int l, int r, int L, int R, ll k) {
ll s = 0;
if(L <= l && r <= R) {
while(Min[i] <= k) {
s += update(i, l, r, k);
}
add[i] -= k;
Min[i] -= k;
return s + num[i] * k;
}
pushdown(i);
int mid = l + r >> 1;
if(L <= mid) s += query(ls, l, mid, L, R, k);
if(R > mid) s += query(rs, mid + 1, r, L, R, k);
pushup(i);
return s;
}
}T;
int main() {
//freopen("fuck.txt", "r", stdin);
//freopen("ans2.txt", "w", stdout);
cin >> n;
rep(i, 1, n) cin >> a[i];
T.build(1, 1, n);
cin >> m;
while(m--) {
cin >> b >> c >> d;
cout << T.query(1, 1, n, b, c, d) << '\n';
}
return 0;
}
然后这里要注意一个很严重的问题!这里的这个 query 函数是带修改的,也有 pushup,千万不要惯性思维!
我也是在这里浪费一个半小时各种对拍造数据才发现自己蠢的没边的。
- T7
猫树分治,然而我不会。有兴趣自己去看P6240应该是一模一样的吧(?