CF2152
CF2152B Catching the Krug
废物hdh
如果一开始 K 和 D 在同一行或者同一列,那么 K 的逃跑方向唯一,D 抓住它的时间就是到那个方向的边界的切比雪夫距离。否则,就是两个逃跑方向的切比雪夫距离取 \(\max\)。
Code
#include <bits/stdc++.h>
using namespace std;
int n, rk, ck, rd, cd;
void solve(){
cin >> n >> rk >> ck >> rd >> cd;
int p = (rd < rk ? n - rd : rd),
q = (cd < ck ? n - cd : cd);
if(rk == rd) cout << q << '\n';
else if(ck == cd) cout << p << '\n';
else cout << max(p, q) << '\n';
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2152C Tripple Removal
判断一个区间是否是 01 交替根本不需要线段树,维护一个 \(c_i = a_i \oplus a_{i - 1}\) 然后看看区间内是否为全 1 即可。
赛时的 sb 代码。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 8e5 + 5;
#define ls(p) p << 1
#define rs(p) p << 1 | 1
int lp[N], rp[N], fl[N], a[N], n, sum[N], q;
void pushup(int p){
fl[p] = 0;
if(fl[ls(p)] && fl[rs(p)]) fl[p] = rp[ls(p)] ^ lp[rs(p)];
lp[p] = lp[ls(p)], rp[p] = rp[rs(p)];
}
void build(int p, int pl, int pr){
if(pl == pr) return lp[p] = rp[p] = a[pl], fl[p] = 1, void();
int mid = (pl + pr) >> 1;
build(ls(p), pl, mid);
build(rs(p), mid + 1, pr);
pushup(p);
}
vector<int> arr;
void get(int p, int pl, int pr, int L, int R){
if(L <= pl && R >= pr) return arr.emplace_back(p), void();
int mid = (pl + pr) >> 1;
if(L <= mid) get(ls(p), pl, mid, L, R);
if(R > mid) get(rs(p), mid + 1, pr, L, R);
}
bool check(int L, int R){
arr.clear();
get(1, 1, n, L, R);
int lst = arr.front();
if(!fl[lst]) return 0;
for(int i = 1; i < arr.size(); ++i){
int p = arr[i];
if(!fl[p]) return 0;
if(!(rp[lst] ^ lp[p])) return 0;
lst = p;
}
return 1;
}
void solve(){
cin >> n >> q;
for(int i = 1; i <= n; ++i){
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
build(1, 1, n);
while(q--){
int l, r; cin >> l >> r;
int num1 = sum[r] - sum[l - 1], num0 = (r - l + 1) - num1;
if(num0 % 3 || num1 % 3) cout << -1 << '\n';
else{
cout << (r - l + 1) / 3 + check(l, r) << '\n';
}
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2152D Division Versus Addition
基础答案是 \(\sum_{i = l}^{r} \lfloor \log_2{a_i} \rfloor\)。
只要 \(\operatorname{popcount}(x) \ge 2\),那么 R 都可以让答案 +1,要特别注意的是,对于 \(\operatorname{popcount}(x) = 2 \land x \text{ is odd}\) 的话,P 可以优先把他右移一位然后 R 就没办法了,这种是要争抢的资源,所以对于这种要除以二上取整从答案减去。一些前缀和维护就行。
赛时到底在犯什么唐以为那种争抢形的资源只用减 1。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2.5e5 + 5;
int a[N], b[N], c[N], d[N], n, q;
void solve(){
cin >> n >> q;
for(int i = 1; i <= n; ++i){
cin >> a[i];
b[i] = b[i - 1] + __lg(a[i]);
int cnt = 0;
for(int j = 0; j < 32; ++j){
if(a[i] & (1 << j)) ++cnt;
}
d[i] = d[i - 1];
if(cnt == 2 && (a[i] & 1)) d[i]++;
c[i] = c[i - 1] + (cnt >= 2);
}
while(q--){
int l, r;
cin >> l >> r;
cout << b[r] - b[l - 1] + c[r] - c[l - 1] - (d[r] - d[l - 1] + 1) / 2 << '\n';
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2152E Monotone Subsequence
清新交互题。第一次先询问 \(1 \sim n\) 的所有数,然后返回一个序列。如果这个序列长度 \(\ge n + 1\) 直接输出,如果没有就把他们都删掉,询问剩下的序列。重复 \(n\) 次。
如果这 \(n\) 次询问中,有任何一次返回的序列 \(\ge n + 1\) 了,就直接输出。否则进行完 \(n\) 次之后一定还剩一些数(至少一个)。对于一个第 \(i\) 次删除的数,找到它之前第一个第 \(i - 1\) 次删除的数,肯定是被它挡住了。由于我们删了 \(n + 1\) 次,所以把刚刚跳的过程反过来,就能找到长度为 \(n + 1\) 的一个递减序列。
这个构造也给出了一种题目中定理的证明方法。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2 + 5;
set<int> s;
int n, a[N * N];
void solve(){
cin >> n;
for(int i = 1; i <= n * n + 1; ++i) s.insert(i);
int cnt = 0;
while(!s.empty()){
cout << "? " << s.size() << ' ';
for(int x : s) cout << x << ' ';
cout << endl;
int k; cin >> k;
vector<int> tmp;
for(int i = 1; i <= k; ++i){
int o; cin >> o;
tmp.emplace_back(o);
}
if(k >= n + 1){
cout << "! ";
for(int i = 0; i < n + 1; ++i) cout << tmp[i] << ' ';
cout << endl;
return;
}
++cnt;
for(int x : tmp){
s.erase(x);
a[x] = cnt;
}
if(cnt == n) break;
}
for(int x : s) a[x] = n + 1;
s.clear();
cout << "! ";
vector<int> ans;
for(int i = 1; i <= n * n + 1; ++i){
if(a[i] == n + 1){
int p = i;
ans.emplace_back(p);
for(int j = n; j >= 1; --j){
int o = p - 1;
while(a[o] != j) --o;
p = o;
ans.emplace_back(p);
}
break;
}
}
reverse(ans.begin(), ans.end());
for(int x : ans) cout << x << ' ';
cout << endl;
}
int main(){
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2152F Tripple Attack
最后要找的这个子序列要满足 \(a_{i + 2} - a_i > z\)。
我们选取 \((l, l + 1)\) 作为序列的两个开头肯定不劣。先考虑单个跳 \(>z\) 的情况,我们可以让 \(l\) 和 \(l + 1\) 分别往后跳,直到他们在 \(x\) 处相遇,类似树上 lca,这期间跳的步数(子序列长度)和跳到哪了都是容易求的。当他们在 \(x\) 处相遇时,我们强制把更靠后的那个引到 \(x + 1\),这样又变成了一个 \((x, x + 1)\) 向后跳的问题。
那么我们对这个结构在进行倍增(也就是 \((l, l + 1) \to (x , x + 1) \cdots\)),知道跳出边界。最后再单个往后跳肯定不会在 \(r\) 之前相交的。
注意一下当 \(l\) 直接跳到 \(l + 1\) 时的特判。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2.5e5 + 5;
int a[N], f[N][21], g[N][21], dep[N], num[N][21], n, z;
int get(int p, int &nxt){
if(p == n) return nxt = p + 1, 1;
int x = p, y = p + 1;
if(f[x][0] == y){ return nxt = y, 1; }
for(int i : {0, 1, 2}){
if(dep[f[x][i]] >= dep[y]) x = f[x][i];
}
for(int i = 19; i >= 0; --i){
if(f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
}
return nxt = f[x][0], dep[p] + dep[p + 1] - 2 * dep[nxt];
}
int query(int l, int r){
int res = 0;
for(int i = 19; i >= 0; --i){
if(g[l][i] <= r){
res += num[l][i];
l = g[l][i];
}
}
if(l == r) return res + 1;
res += 2;
int x = l, y = l + 1;
for(int i = 19; i >= 0; --i){
if(f[x][i] <= r){
res += (1 << i);
x = f[x][i];
}
if(f[y][i] <= r){
res += (1 << i);
y = f[y][i];
}
}
return res;
}
void solve(){
cin >> n >> z;
for(int i = 1; i <= n; ++i) cin >> a[i];
int j = 1;
for(int i = 1; i <= n; ++i){
while(j <= n && a[j] <= a[i] + z) ++j;
f[i][0] = j;
}
for(int i = n; i >= 1; --i) dep[i] = dep[f[i][0]] + 1;
for(int i = 0; i <= 19; ++i) g[n + 1][i] = f[n + 1][i] = n + 1;
for(int j = 1; j <= 19; ++j){
for(int i = 1; i <= n; ++i){
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
for(int i = 1; i <= n; ++i){
num[i][0] = get(i, g[i][0]);
// cout << i << ' ' << g[i][0] << ' ' << num[i][0] << '\n';
}
for(int j = 1; j <= 19; ++j){
for(int i = 1; i <= n; ++i){
num[i][j] = num[i][j - 1] + num[g[i][j - 1]][j - 1];
g[i][j] = g[g[i][j - 1]][j - 1];
}
}
int q; cin >> q;
while(q--){
int l, r; cin >> l >> r;
if(l == n) cout << "1\n";
else cout << query(l, r) << '\n';
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2152G Query Jungle
一个括号序的应用。括号序是 dfs 进来的时候加左括号,出去的时候加右括号。区别欧拉序是每进来一次都加一次。
题目要求的是有多少个 \(a_i = 1\) 的点的子树内没有任何 1。那么我们只看 1 的那些点形成的括号序,等价于问有多少个 ()。
考虑线段树。维护区间最左/右边的 \(a_i = 0/1\) 的点是左括号还是右括号,和 \(a_i = 0/1\) 的答案,这样区间反转就是交换 0 / 1 了。这种区间反转的都得对称的维护一些东西。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
#define ls(p) p << 1
#define rs(p) p << 1 | 1
int b[N], a[N], n, c[N], l[N], r[N], tsp, tag[N * 4], lp[N * 4][2], rp[N * 4][2], ans[N * 4][2];
vector<int> e[N];
void dfs(int u, int fa){
b[++tsp] = 0;
c[tsp] = u;
l[u] = tsp;
for(int v : e[u]){
if(v != fa) dfs(v, u);
}
b[++tsp] = 1;
c[tsp] = u;
r[u] = tsp;
}
void pushup(int p){
for(int j : {0, 1}){
ans[p][j] = ans[ls(p)][j] + ans[rs(p)][j] + (rp[ls(p)][j] == 0 && lp[rs(p)][j] == 1);
lp[p][j] = (lp[ls(p)][j] == -1 ? lp[rs(p)][j] : lp[ls(p)][j]);
rp[p][j] = (rp[rs(p)][j] == -1 ? rp[ls(p)][j] : rp[rs(p)][j]);
}
}
void addtag(int p){
tag[p] ^= 1;
swap(lp[p][0], lp[p][1]);
swap(rp[p][0], rp[p][1]);
swap(ans[p][0], ans[p][1]);
}
void pushdown(int p){
if(tag[p]){
addtag(ls(p));
addtag(rs(p));
tag[p] = 0;
}
}
void build(int p = 1, int pl = 1, int pr = 2 * n){
ans[p][0] = ans[p][1] = tag[p] = 0;
if(pl == pr){
int vl = a[c[pl]];
lp[p][vl] = rp[p][vl] = b[pl];
lp[p][vl ^ 1] = rp[p][vl ^ 1] = -1;
return;
}
int mid = (pl + pr) >> 1;
build(ls(p), pl, mid);
build(rs(p), mid + 1, pr);
pushup(p);
}
void flip(int L, int R, int p = 1, int pl = 1, int pr = 2 * n){
if(L <= pl && R >= pr) return addtag(p);
int mid = (pl + pr) >> 1;
pushdown(p);
if(L <= mid) flip(L, R, ls(p), pl, mid);
if(R > mid) flip(L, R, rs(p), mid + 1, pr);
pushup(p);
}
void solve(){
cin >> n;
for(int i = 1; i <= n; ++i) e[i].clear(), cin >> a[i];
for(int i = 1; i < n; ++i){
int u, v; cin >> u >> v;
e[u].emplace_back(v);
e[v].emplace_back(u);
}
tsp = 0;
dfs(1, 0);
build();
assert(tsp == 2 * n);
cout << ans[1][1] << '\n';
int q; cin >> q;
while(q--){
int u; cin >> u;
flip(l[u], r[u]);
cout << ans[1][1] << '\n';
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号