Educational Codeforces Round 23
A. Treasure Hunt
#include <bits/stdc++.h>
using namespace std;
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int main(){
int a , b , c , d , x , y;
cin >> a >> b >> c >> d >> x >> y;
a = abs( a - c ) , b = abs( b - d );
if( a % x == 0 && b % y == 0 && (a / x)%2 == (b / y)%2 )
cout << "YES\n";
else
cout << "NO\n";
return 0;
}
B. Makes And The Product
排个序,然后取最小的三个数,然后分类讨论一下就好了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int32_t main() {
int n = read();
vector<int> a(n);
map<int, int> cnt;
for (auto &i: a)
i = read(), cnt[i]++;
sort(a.begin(), a.end());
int x = a[0], y = a[1], z = a[2];
if (x == y && y == z) {
int m = cnt[x];
cout << m * (m - 1) * (m - 2) / 6;
} else if (x == y && y != z) {
int p = cnt[x], q = cnt[z];
cout << p * (p - 1) / 2 * q;
} else if (x != y && y == z) {
int p = cnt[x], q = cnt[z];
cout << p * q * (q - 1) / 2;
} else
cout << cnt[x] * cnt[y] * cnt[z];
return 0;
}
C. Really Big Numbers
定义\(x\)每一位的和是\(f(x)\),则求在\([1,n]\)中\(x - f(x) \ge s\)的个数。
首先如果\(a\)满足\(a-f(a)\ge s\),则\(a+1 - f(a+1) >= s\)一定满足。所以我们直接二分的找到最小的\(x\)即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int32_t main() {
auto f = [](int x){
int ans = 0;
while( x ) ans += x % 10 , x /= 10;
return ans;
};
int n , s;
cin >> n >> s;
int l = 1 , r = n , mid , ans = -1;
while( l <= r ){
mid = ( l + r ) >> 1;
if( mid - f(mid) >= s ) ans = mid , r = mid - 1;
else l = mid + 1;
}
if( ans == -1 ) cout << 0 << "\n";
else cout << n - ans + 1;
return 0;
}
D. Imbalanced Array
这题最朴素的想法是枚举区间,然后统计最值,复杂度\(O(N^3)\).
很容易想到用ST表优化到\(O(N^2)\).
这样的思路其实是枚举区间,我们其实可以换一个角度去思考,对于\(a_i\)在哪些区间里面做最值。
以最大值为例,如果我们找到\(i\)做侧第一个比\(a_i\)大的值\(a_l\)和右侧的第一个\(a_r\)其实就是\([l+1,r-1]\)之间左右包含\(i\)的区间的最大值都是\(a_i\).
这样的话,用 ST表配合二分求出左右共四个端点即可,复杂度\(O(N\log N)\).
这里求最值的话也可利用单调栈的性质优化到\(O(N)\).
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
int32_t main() {
int n = read();
vector<int> a(n + 2);
vector<int> maxL(n + 1), maxR(n + 1), minL(n + 1), minR(n + 1);
for (int i = 1; i <= n; i++) a[i] = read();
a[0] = a[n + 1] = LLONG_MAX;
// 左侧最大
stack<int> s;
s.push(0);
for (int i = 1; i <= n; i++) {
while (a[i] >= a[s.top()]) s.pop();
maxL[i] = i - s.top(), s.push(i);
}
// 右侧最大
s = stack<int>(), s.push(n + 1);
for (int i = n; i >= 1; i--) {
while (a[i] > a[s.top()]) s.pop();
maxR[i] = s.top() - i, s.push(i);
}
// 左侧最小
a[0] = a[n + 1] = LLONG_MIN;
s = stack<int>(), s.push(0);
for (int i = 1; i <= n; i++) {
while (a[i] <= a[s.top()]) s.pop();
minL[i] = i - s.top(), s.push(i);
}
// 右侧最小
s = stack<int>(), s.push(n + 1);
for (int i = n; i >= 1; i--) {
while (a[i] < a[s.top()]) s.pop();
minR[i] = s.top() - i, s.push(i);
}
int res = 0;
for (int i = 1; i <= n; i++)
res += (maxL[i] * maxR[i] - minL[i] * minR[i]) * a[i];
printf("%lld\n", res);
return 0;
}
E. Choosing The Commander
用01Tire维护当前集合中的数,注意在插入的时候从二进制的高位开始插入。
对于查询操作,我们沿着\(p_i \oplus l_i\)的路径去遍历前缀,如果对于当前的节点向另一个方向走可以使得\(p_x\oplus p_i < l_i\),就加上另一个子树的权值即可。
#include <bits/stdc++.h>
using namespace std;
typedef bitset<30> Num;
class Tire {
private:
struct Node {
int v, cnt, size, to0, to1;
Node(int v = -1, int cnt = 0, int to0 = -1, int to1 = -1) : v(v), cnt(cnt), to0(to0), to1(to1) {
size = 0;
};
};
vector<Node> T;
public:
Tire() {
T.push_back(Node());
}
void insert(Num x) {
vector<int> road;
int pos = 0;
for (int i = 29; i >= 0; i--) {
if (x[i] == 0) {
if (T[pos].to0 == -1) T[pos].to0 = T.size(), T.emplace_back(0);
pos = T[pos].to0;
} else {
if (T[pos].to1 == -1) T[pos].to1 = T.size(), T.emplace_back(1);
pos = T[pos].to1;
}
road.push_back(pos);
}
T[pos].cnt++;
for (auto i: road) T[i].size++;
}
void del(Num x) {
vector<int> road;
int pos = 0;
for (int i = 29; i >= 0; i--) {
if (x[i] == 0) pos = T[pos].to0;
else pos = T[pos].to1;
road.push_back(pos);
}
T[pos].cnt--;
for (auto i: road) T[i].size--;
}
int query(Num x, Num y) {
int ans = 0, pos = 0;
for (int i = 29, p; i >= 0; i--) {
if (y[i] == 1) {
if (x[i] == 0) p = T[pos].to0;
else p = T[pos].to1;
if (p != -1) ans += T[p].size;
}
if (x[i] ^ y[i] == 0) pos = T[pos].to0;
else pos = T[pos].to1;
if (pos == -1) break;
}
return ans;
}
};
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int q;
cin >> q;
Tire tire;
for (int op, p, l; q; q--) {
cin >> op >> p;
if (op == 1) tire.insert(Num(p));
else if (op == 2)tire.del(Num(p));
else {
cin >> l;
cout << tire.query(Num(p), Num(l)) << "\n";
}
}
return 0;
}
F. MEX Queries
把这个题当做有一个大小为\(10^{18}\)的数组,数组一开始全为 0,有三种操作,区间赋1、区间赋 0、区间取反,每次操作后询问最靠左的 0 的下标。
这样就会发现这是 ODT 的模板题,mex就是第一个值为 0 的区间的左端点。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read() {
int x = 0, f = 1, ch = getchar();
while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
if (ch == '-') f = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
class ODT {
private:
struct Node {
int l, r;
mutable int val;
Node(int l, int r = 0, int val = 0) : l(l), r(r), val(val) {};
bool operator<(Node b) const {
return l < b.l;
}
int len() const {
return r - l + 1;
}
};
const int len = 1e18;
set<Node> s;
public:
ODT() {
s.insert(Node(1, len, 0));
}
auto split(int x) {
if (x > len) return s.end();
auto it = --s.upper_bound(Node(x));
if (it->l == x) return it;
int l = it->l, r = it->r, v = it->val;
s.erase(it);
s.insert(Node(l, x - 1, v));
return s.insert(Node(x, r, v)).first;
}
void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
s.erase(itl, itr);
s.insert(Node(l, r, v));
}
void invert(int l, int r) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++)
it->val ^= 1;
}
int mex() {
for (auto it: s) {
if (it.val == 0) return it.l;
}
return len + 1;
}
};
int32_t main() {
int q = read();
ODT odt;
for( int op , l , r ; q ; q -- ){
op = read() , l = read() , r = read() ;
if( op == 1 )
odt.assign( l , r , 1 );
else if( op == 2 )
odt.assign( l , r , 0 );
else odt.invert( l , r );
printf("%lld\n" , odt.mex() );
}
return 0;
}