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

\( \sum\limits_{k=0}^n \operatorname{popcount}(k\&m) = \sum\limits_{k=0}^n\sum\limits_{i = 0}^{59} [\frac{k\& m}{2^i} = 1] = \sum\limits_{i = 0}^{59}\sum\limits_{k=0}^n [\frac{k\& m}{2^i} = 1] \)

其中,\(\sum\limits_{k=0}^n [\frac{k\& m}{2^i} = 1]\) 表示使得 \(k\& m\) 的第 \(i\) 位是 \(1\)\(k\) 的个数
容易发现只有 \(m\) 中的 1 所在的位置才会产生贡献

考虑第 \(i\) 位,可以发现 \(2^i\)\(0\)\(2^i\)\(1\) 交替出现
那么周期就是 \(2^{i+1}\),那么一个完整周期的贡献就是 \(2^i\),而剩下不足一个周期的部分(假设为 \(r\))的贡献就是 \(\max(0, r-2^i+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\) 做一遍排序
这样原式就变成了 \(\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^n \lfloor \frac{A_j}{A_i} \rfloor\)
注意到 \(\sum \lfloor \frac{A_j}{A_i} \rfloor\) 等价于求 \(\sum\limits_x \lfloor \frac{A_j}{A_i} \rfloor = x\)\(j\) 的个数 \(\times x\),其中 \(x \in [1, \lfloor\frac{10^6}{A_i}\rfloor]\)
是个调和级数

\(\lfloor \frac{A_j}{A_i} \rfloor = x \Rightarrow A_ix \leqslant A_j < A_i(x+1)\),刚好是区间和,所以可以用前缀和来加速

另外第一重求和的变量 \(i\) 可以换成 \(y\),这样就变成了 \(\sum\limits_{y=1}^{M}(\sum\limits_x \lfloor \frac{A_j}{y} \rfloor = x\)\(j\) 的个数 \(\times x) \times\) 满足 \(A_i=y\)\(i\) 的个数,其中 \(M=10^6\)
那么,时间复杂度就是 \(\frac{M}{1} + \frac{M}{2} + \cdots + \frac{M}{M} = \mathcal{O}(M\log M)\)

代码实现
#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\) \(\Rightarrow\) std::set
  • \(S\) 里绝对差大于 \(k\) 的位置 \(\Rightarrow\) std::set
  • 区间里的点的个数 \(\Rightarrow\) 离散化 + 树状数组
代码实现
#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

求凸包和直线交点的问题