A. Subsegment Reverse

模拟

代码实现
n, l, r = map(int, input().split())
l -= 1
a = list(range(1, n+1))
a[l:r] = a[l:r][::-1]
print(*a)

B. Nutrients

模拟

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(m);
rep(i, m) cin >> a[i];
vector x(n, vector<int>(m));
rep(i, n)rep(j, m) cin >> x[i][j];
vector<int> s(m);
rep(i, n)rep(j, m) {
s[j] += x[i][j];
}
rep(i, m) {
if (s[i] < a[i]) {
puts("No");
return 0;
}
}
puts("Yes");
return 0;
}

C.Keys

二进制枚举

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m, k;
cin >> n >> m >> k;
vector<int> as(m);
vector<char> r(m);
rep(i, m) {
int c;
cin >> c;
rep(j, c) {
int a;
cin >> a;
--a;
as[i] |= 1<<a;
}
cin >> r[i];
}
int ans = 0;
rep(s, 1<<n) {
bool ok = true;
rep(i, m) {
int num = __builtin_popcount(as[i]&s);
if ((num >= k) != (r[i] == 'o')) ok = false;
}
if (ok) ans++;
}
cout << ans << '\n';
return 0;
}

D. Masked Popcount

k=0npopcount(k&m)=k=0ni=059[k&m2i=1]=i=059k=0n[k&m2i=1]

其中,k=0n[k&m2i=1] 表示使得 k&m 的第 i 位是 1k 的个数
容易发现只有 m 中的 1 所在的位置才会产生贡献

考虑第 i 位,可以发现 2i02i1 交替出现
那么周期就是 2i+1,那么一个完整周期的贡献就是 2i,而剩下不足一个周期的部分(假设为 r)的贡献就是 max(0,r2i+1)

代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using mint = modint998244353;
int main() {
ll n, m;
cin >> n >> m;
n++;
mint ans;
rep(i, 60) {
if (m>>i&1) {
ll p = 2ll<<i;
ll r = n%p;
ans += (n-r)/2;
if (r >= (1ll<<i)) {
ans += r-(1ll<<i);
}
}
}
cout << ans.val() << '\n';
return 0;
}

E. Max/Min

容易发现,答案和序列 A 的顺序无关,所以我们可以先对序列 A 做一遍排序
这样原式就变成了 i=1n1j=i+1nAjAi
注意到 AjAi 等价于求 xAjAi=xj 的个数 ×x,其中 x[1,106Ai]
是个调和级数

AjAi=xAixAj<Ai(x+1),刚好是区间和,所以可以用前缀和来加速

另外第一重求和的变量 i 可以换成 y,这样就变成了 y=1M(xAjy=xj 的个数 ×x)× 满足 Ai=yi 的个数,其中 M=106
那么,时间复杂度就是 M1+M2++MM=O(MlogM)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int M = 1e6;
int main() {
int n;
cin >> n;
vector<int> c(M+1);
rep(i, n) {
int a;
cin >> a;
c[a]++;
}
vector<int> s(M+2);
rep(i, M+1) s[i+1] = s[i]+c[i];
auto sum = [&](int l, int r) {
r = min(M+1, r);
return s[r]-s[l];
};
ll ans = 0;
for (int y = 1; y <= M; ++y) {
// 这里每个 y 自己会和自己相乘,会变成 c[y]*c[y] 次,所以提前容斥掉多余的部分
ans -= (ll)c[y]*(c[y]+1)/2;
ll now = 0;
for (int x = 1; x*y <= M; ++x) {
int l = y*x, r = y*(x+1);
now += (ll)sum(l, r)*x;
}
ans += now*c[y];
}
cout << ans << '\n';
return 0;
}

F. Distance Component Size Query

先对集合中的数做一遍排序,然后相邻两个数如果满足绝对差不超过 k 则可以连边。对于询问 2,可以从 x 出发分别向两边跳,一直跳到当前数和下一个数没有边时停止

需要能实现以下功能的数据结构

  • 集合 S std::set
  • S 里绝对差大于 k 的位置 std::set
  • 区间里的点的个数 离散化 + 树状数组
代码实现
#include <bits/stdc++.h>
#if __has_include(<atcoder/all>)
#include <atcoder/all>
using namespace atcoder;
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
// Coodinate Compression
template<typename T=int>
struct CC {
bool initialized;
vector<T> xs;
CC(): initialized(false) {}
void add(T x) { xs.push_back(x);}
void init() {
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(),xs.end()),xs.end());
initialized = true;
}
int operator()(T x) {
if (!initialized) init();
return upper_bound(xs.begin(), xs.end(), x) - xs.begin() - 1;
}
T operator[](int i) {
if (!initialized) init();
return xs[i];
}
int size() {
if (!initialized) init();
return xs.size();
}
};
int main() {
int q; ll k;
cin >> q >> k;
vector<pair<int, ll>> qs;
CC<ll> cc;
rep(qi, q) {
int type; ll x;
cin >> type >> x;
qs.emplace_back(type, x);
cc.add(x);
}
const ll INF = 3e18;
cc.add(-INF);
cc.add(INF);
set<ll> s;
s.insert(-INF); s.insert(INF);
set<ll> gap;
gap.insert(-INF); gap.insert(INF);
fenwick_tree<int> d(cc.size());
for (auto [type, x] : qs) {
if (type == 1) {
if (s.count(x)) {
auto it = s.find(x);
s.erase(it--);
gap.erase(x);
ll l = *it;
it++; ll r = *it;
if (r-l > k) gap.insert(l);
d.add(cc(x), -1);
}
else {
auto it = s.lower_bound(x);
ll r = *it;
it--; ll l = *it;
gap.erase(l);
s.insert(x);
if (x-l > k) gap.insert(l);
if (r-x > k) gap.insert(x);
d.add(cc(x), 1);
}
}
else {
auto it = gap.lower_bound(x);
ll r = *it;
it--; ll l = *it;
int ans = d.sum(cc(l)+1, cc(r)+1);
cout << ans << '\n';
}
}
return 0;
}

G.Freestyle

求凸包和直线交点的问题