A Candy Cookie Law
判断一下即可,注意没有 $spj$ ,犯法输出 $Yes$ ,没犯法输出 $No$ 。
 void solve() {
     int a, b, c, d;
     cin >> a >> b >> c >> d;
     if (c >= a && d >= b || c < a) {
         cout << "No" << "\n";
     } else {
         cout << "Yes" << "\n";
     }
 }
B Count Subgrid
暴力枚举每个 $m\times m$ 的方格即可,转化成二进制或者字符串去重,注意转化成二进制时范围会到 $2^{100}$ ,需要用 $int128$ 。
 void solve() {
     int n, m;
     cin >> n >> m;
     vector<vector<int>> g(n, vector<int>(n));
     for (int i = 0; i < n; ++i) {
         string s;
         cin >> s;
         for (int j = 0; j < n; ++j) {
             g[i][j] = (s[j] == '.' ? 0 : 1);
         }
     }
     vector<i128> st;
     for (int i = 0; i + m <= n; ++i) {
         for (int j = 0; j + m <= n; ++j) {
             i128 cur = 0;
             for (int k = i; k < i + m; ++k) {
                 for (int l = j; l < j + m; ++l) {
                     cur = (cur << 1) + g[k][l];
                 }
             }
             st.pb(cur);
         }
     }
     sort(all(st));
     st.erase(unique(all(st)), st.end());
     cout << st.size() << "\n";
 }
C Truck Driver
求关于 $a$ 和 $b$ 的前缀,对于每个起点 $i$ ,二分对于 $a$ 的个数合法的 $r1$ 和对于 $b$ 的个数合法的 $r2$ ,二者相减就是合法的种数。
 void solve() {
     int n, a, b;
     cin >> n >> a >> b;
     string s;
     cin >> s;
     vector<int> sa(n + 1), sb(n + 1);
     for (int i = 1; i <= n; ++i) {
         sa[i] = sa[i - 1] + (s[i - 1] == 'a');
         sb[i] = sb[i - 1] + (s[i - 1] == 'b');
     }
     ll ans = 0;
     for (int i = 1; i <= n; ++i) {
         auto it1 = lower_bound(sa.begin() + i, sa.end(), sa[i - 1] + a);
         if (it1 == sa.end()) continue;
         int r1 = it1 - sa.begin();
 
         auto it2 = lower_bound(sb.begin() + i, sb.end(), sb[i - 1] + b);
         int r2;
         if (it2 == sb.end()) r2 = n + 1;
         else r2 = it2 - sb.begin();
 
         if (r1 <= r2 - 1) {
             ans += r2 - r1;
         }
     }
     cout << ans << "\n";
 }
D Neighbor Distance
每次插入一个新点 $x$ ,它只会影响自己和它相邻的两个点(前驱、后继)的最近距离。 因为别的点离它更远,不可能改变它们的最近邻。 所以我们用一个有序集合保存所有坐标,每次插入后只更新这三个点的 $d$ 值,并维护全局和。
 void solve() {
     int n;
     cin >> n;
     vector<ll> a(n);
     for (int i = 0; i < n; ++i) cin >> a[i];
 
     set<ll> pos;
     unordered_map<ll, ll> d;
     pos.insert(0);
     d[0] = 0;
     ll S = 0;
 
     for (int i = 0; i < n; ++i) {
         ll x = a[i];
         auto it = pos.lower_bound(a[i]);
         bool f1 = (it != pos.end()), f2 = (it != pos.begin());
 
         auto pref = f2 ? prev(it) : pos.end();
 
         if (f2) {
             ll pred = *pref;
             ll old = d[pred];
             ll ppd = INF;
             if (pref != pos.begin()) {
                 auto pp = prev(pref);
                 ppd = pred - *pp;
             }
             ll npd = min(ppd, a[i] - pred);
             S += (npd - old);
             d[pred] = npd;
         }
 
         if (f1) {
             ll succ = *it;
             ll old = d[succ];
             ll nnd = INF;
             auto nit = next(it);
             if (nit != pos.end()) nnd = *nit - succ;
             ll nsd = min(nnd, succ - a[i]);
             S += (nsd - old);
             d[succ] = nsd;
         }
 
         ll l = f2 ? a[i] - *pref : INF;
         ll r = f1 ? *it - a[i] : INF;
         ll nxd = min(l, r);
         d[a[i]] = nxd;
         S += nxd;
         pos.insert(a[i]);
 
         cout << S << "\n";
     }
 }
E Shift String
先判断 $A$ 和 $B$ 的字符个数(0/1)是否相同,若不同直接输出 $-1$ 。
把 $A$ 拼成 $A+A$ ,在这个长度为 $2n$ 的字符串中查找模式 $B$ 。
找到的第一个匹配起始位置 $k$(且 $k < n$ )就是最小左旋次数,若没有这样的匹配则输出 $-1$ 。
 void solve() {
     string a, b;
     cin >> a >> b;
     int n = a.size();
     if (b.size() != n) {
         cout << -1 << "\n";
         return;
     }
 
     int ca[2] = {0, 0}, cb[2] = {0, 0};
     for (char c : a) ca[c - '0']++;
     for (char c : b) cb[c - '0']++;
     if (ca[0] != cb[0] || ca[1] != cb[1]) {
         cout << -1 << "\n";
         return;
     }
 
     string A = a + a;
     int m = n;
 
     vector<int> pi(m, 0);
     for (int i = 1; i < m; ++i) {
         int j = pi[i - 1];
         while (j > 0 && b[i] != b[j]) j = pi[j - 1];
         if (b[i] == b[j]) ++j;
         pi[i] = j;
     }
 
     int j = 0;
     int ans = -1;
 
     for (int i = 0; i < A.size(); ++i) {
         while (j > 0 && A[i] != b[j]) j = pi[j - 1];
         if (A[i] == b[j]) ++j;
         if (j == m) {
             int st = i - m + 1;
             if (st < n) {
                 ans = st;
                 break;
             }
             j = pi[j - 1];
         }
     }
 
     if (ans == -1) cout << -1 << "\n";
     else cout << ans << "\n";
 
 }
F Back and Forth Filling
对于每一个位置的数字,都可以求出它的最左位置 $mn$ 和最右位置 $mx$ ,使得它可以放在 $[mn,mx]$ 的任意一个位置。
- $mn = 1+\text{左边连续的}l\text{的数量}+\text{右边连续的}r\text{的数量}$
 - $mn = n-\text{左边连续的}l\text{的数量}-\text{右边连续的}r\text{的数量}$
 
用差分数组统计统计每个位置被多少个区间覆盖即可。
 void solve() {
     int n;
     string s;
     cin >> n >> s;
 
     vector<int> lr(n + 1, 0), rl(n + 2, 0), ll(n + 1, 0), rr(n + 2, 0);
 
     for (int v = 2; v <= n; ++v) {
         if (s[v - 2] == 'R') lr[v] = lr[v - 1] + 1;
         else lr[v] = 0;
     }
 
     for (int v = n - 1; v >= 1; --v) {
         if (s[v - 1] == 'L') rl[v] = rl[v + 1] + 1;
         else rl[v] = 0;
     }
 
     for (int v = 2; v <= n; ++v) {
         if (s[v - 2] == 'L') ll[v] = ll[v - 1] + 1;
         else ll[v] = 0;
     }
 
     for (int v = n - 1; v >= 1; --v) {
         if (s[v - 1] == 'R') rr[v] = rr[v + 1] + 1;
         else rr[v] = 0;
     }
 
     vector<int> diff(n + 3, 0);
     for (int v = 1; v <= n; ++v) {
         int mn = 1 + lr[v] + rl[v];
         int mx = n - (rr[v] + ll[v]);
         diff[mn]++;
         diff[mx + 1] --;
     }
 
 
     vector<int> ans(n + 1, 0);
     int cur = 0;
     for (int i = 1; i <= n; ++i) {
         cur += diff[i];
         ans[i] = cur;
     }
 
     for (int i = 1; i <= n; ++i) {
         cout << ans[i] << " ";
     }
     cout << "\n";
 }
G Range Set Modifying Query
用线段树维护每个位置 $|S_i|$ ,节点保存区间最大值 $mx$ 和出现次数 $cnt$ ,支持区间加/减的懒标记。
对每个元素值 $x(1\cdots 60)$单独维护一个不相交的区间集合,表示哪些下标当前包含 $x$(区间合并/拆分,避免逐点操作)。
加 $x$ 到 $[L,R]$ :先对线段树 $+1$ ,然后对与 $[L,R]$ 重叠的已有区间只撤销它们的交集(对交集区间做 $-1$ ),最后将这些区间与 $[L,R]$ 合并插回集合。
删 $x$ 从 $[L,R]$ :找到与 $[L,R]$ 重叠的区间,对交集区间做 $-1$ ,并把被截断的左右残段重新插回集合(区间拆分)。
查询直接用线段树 $ask(L,R)$ 返回 $(mx,cnt)$ 即可。
 void solve() {
     int n, q;
     cin >> n >> q;
 
     SegTree<int> seg(n);
     vector<int> init(n + 1, 0);
     seg.build(init);
 
     vector<set<PII>> ones(maxn + 1);
 
     for (int i = 0; i < q; ++i) {
         int op; cin >> op;
         if (op == 1) {
             int l, r, x;
             cin >> l >> r >> x;
             if (l > r) continue;
             seg.update(l, r, Tag<int> {1});
             auto &S = ones[x];
             auto it = S.lower_bound({l + 1, -inf});
             if (it != S.begin()) {
                 --it;
                 if (it->se < l) ++it;
             }
             int nl = l, nr = r;
             vector<PII> del;
             while (it != S.end() && it->fi <= r) {
                 int a = it->fi, b = it->se;
                 int ovl = max(a, l), ovr = min(b, r);
                 if (ovl <= ovr) seg.update(ovl, ovr, Tag<int> { -1});
                 nl = min(nl, a);
                 nr = max(nr, b);
                 del.pb(*it);
                 ++it;
             }
             for (auto &p : del) S.erase(p);
             S.insert({nl, nr});
         }
         else if (op == 2) {
             int l, r, x; cin >> l >> r >> x;
             if (l > r) continue;
             auto &S = ones[x];
             auto it = S.lower_bound({l + 1, -inf});
             if (it != S.begin()) {
                 --it;
                 if (it->se < l) ++it;
             }
             vector<PII> del;
             vector<PII> add;
             while (it != S.end() && it->fi <= r) {
                 int a = it->fi, b = it->se;
                 int ovl = max(a, l), ovr = min(b, r);
                 if (ovl <= ovr) seg.update(ovl, ovr, Tag<int> { -1});
                 del.pb(*it);
                 if (a < ovl) add.pb({a, ovl - 1});
                 if (ovr < b) add.pb({ovr + 1, b});
                 ++it;
             }
             for (auto &p : del) S.erase(p);
             for (auto &p : add) S.insert(p);
         }
         else if (op == 3) {
             int l, r;
             cin >> l >> r;
             auto res = seg.ask(l, r);
             cout << res.val << " " << res.cnt << "\n";
         }
     }
 }
头文件
//Anoth3r
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef __int128 i128;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef tuple<ll, ll, ll> TLLL;
typedef __gnu_pbds::tree<PLL, __gnu_pbds::null_type, less<PLL>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update> Tree;
// typedef __gnu_pbds::tree<ll, __gnu_pbds::null_type, less<ll>, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update> Tree;
constexpr int inf = (ll)1e9 + 7;
constexpr ll INF = (ll)2e18 + 9;
// constexpr ll INF = (ll)4e18;
// constexpr ll MOD = 1e9 + 7;
constexpr ll MOD = 998244353;
constexpr ld PI = acos(-1.0);
constexpr ld eps = 1e-10;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
ull randint(ull l, ull r) {uniform_int_distribution<unsigned long long> dist(l, r); return dist(rng);}
void init() {
}
void solve() {
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
init();
int t = 1;
// cin >> t;
for (int _ = 1; _ <= t; ++_) {
solve();
}
return 0;
}
线段树
template<class Int>
struct Tag {
Int v = 0;
void operator+=(const Tag<Int> &o) {
v += o.v;
}
bool check() {
return v != 0;
}
};
template<class Int>
struct Info {
Int val = 0;
int cnt = 1;
int l = 0, r = 0;
Info() { val = 0; cnt = 1; l = r = 0; }
Info(int) { val = -inf; cnt = 0; l = r = 0; }
Info operator+(const Info<Int> &o) const {
Info res;
res.l = l;
res.r = o.r;
if (val > o.val) {
res.val = val;
res.cnt = cnt;
} else if (val < o.val) {
res.val = o.val;
res.cnt = o.cnt;
} else {
res.val = val;
res.cnt = cnt + o.cnt;
}
return res;
}
void operator+=(const Tag<Int> &o) {
val += o.v;
}
bool check() {
return l != r;
}
};
template<class Int>
class SegTree {
private:
vector<Info<Int>> info;
vector<Tag<Int>> tag;
int n;
int ls(int x) {return x << 1;}
int rs(int x) {return x << 1 | 1;}
void print(int x, int l, int r) {
cout << x << ":[" << l << "," << r << "],val:" << info[x].val << ",cnt:" << info[x].cnt << ",tag:" << tag[x].v << "\n";
if (l == r) return;
int mid = l + r >> 1;
print(ls(x), l, mid);
print(rs(x), mid + 1, r);
}
template<class Array>
void build(int x, int l, int r, Array &data) {
info[x].l = l;
info[x].r = r;
if (l == r) {
info[x].val = data[l];
return;
}
int mid = (l + r) >> 1;
build(ls(x), l, mid, data);
build(rs(x), mid + 1, r, data);
info[x] = info[ls(x)] + info[rs(x)];
}
void push_down(int x) {
if (tag[x].check() && info[x].check()) {
info[ls(x)] += tag[x];
info[rs(x)] += tag[x];
tag[ls(x)] += tag[x];
tag[rs(x)] += tag[x];
tag[x] = {0};
}
}
void update(int x, int l, int r, int lq, int rq, Tag<Int> v) {
if (rq < l || lq > r) return;
if (lq <= l && r <= rq) {
info[x] += v;
tag[x] += v;
return;
}
push_down(x);
int mid = (l + r) >> 1;
update(ls(x), l, mid, lq, rq, v);
update(rs(x), mid + 1, r, lq, rq, v);
info[x] = info[ls(x)] + info[rs(x)];
}
void modify(int x, int l, int r, int pos, Int v) {
if (r < pos || l > pos) return;
if (l == r && l == pos) {
info[x].val = v;
return;
}
int mid = (l + r) >> 1;
push_down(x);
modify(ls(x), l, mid, pos, v);
modify(rs(x), mid + 1, r, pos, v);
info[x] = info[ls(x)] + info[rs(x)];
}
Info<Int> ask(int x, int l, int r, int lq, int rq) {
if (rq < l || lq > r) return {0};
if (lq <= l && r <= rq) return info[x];
push_down(x);
int mid = (l + r) >> 1;
auto left = ask(ls(x), l, mid, lq, rq);
auto right = ask(rs(x), mid + 1, r, lq, rq);
if (left.cnt == 0) return right;
if (right.cnt == 0) return left;
return left + right;
}
public:
SegTree(int n_): n(n_), info(4 * n_ + 1), tag(4 * n_ + 1) {}
void printTree() {
print(1, 1, n);
}
template<class Array>
void build(Array &data) {
build(1, 1, n, data);
}
void update(int l, int r, Tag<Int> v) {
if (l > r) return;
update(1, 1, n, l, r, v);
}
void modify(int pos, Int v) {
modify(1, 1, n, pos, v);
}
Info<Int> ask(int l, int r) {
return ask(1, 1, n, l, r);
}
};