MX galaxy Day18
stream
\(m\) 条限制看做 \(x_i\leftrightarrow y_i\) 的一条权值为 \(z_i\) 的边。
对于每个连通块单独考虑,取连通块内编号最小的点作为根,处理出每个点与 \(rt\) 的异或值 \(y\) 。
假设合法。
然后每条限制形如 \(rt\oplus y\ge l\) 或者 \(rt\oplus y\le r\) 。
将这些限制分讨插入 \(Tire\) 树,暗位贪心。
具体的,如果我们要求 \(rt\oplus y \le r\) ,且 \(r\) 的最高位是 \(0\) ,那么当前位 \(rt\oplus y=1\) 的子树就被割掉了。
然后再在 \(Trie\) 树上找到一组字典序最小的合法解即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
const int V = 30;
typedef long long ll;
int c, T, n, m, l[_], r[_], dis[_], a[_]; bool vis[_];
int ch[_][2], idx; bool tag[_];
std::vector <int> e[_], g[_], rg;
bool dfs(int u) {
rg.push_back(u), vis[u] = true;
lep(k, 0, (int)e[u].size() - 1) { int v = e[u][k], w = g[u][k];
if (!vis[v]) {
dis[v] = (dis[u] ^ w);
if (!dfs(v)) return false;
}
else if (dis[v] != (dis[u] ^ w)) return false;
}
return true;
}
int rc() { ++idx; ch[idx][0] = ch[idx][1] = tag[idx] = 0; return idx; }
void ins(int y, int v, int op) {
int x = 0;
rep(k, V, 0) { int _y = (y >> k) & 1, _v = (v >> k) & 1;
if (tag[x]) return;
if (op == 0) {
if (!ch[x][_y]) ch[x][_y] = rc();
if (!_v) x = ch[x][_y];
else {
tag[ch[x][_y]] = true;
if (!ch[x][1 ^ _y]) ch[x][1 ^ _y] = rc();
x = ch[x][1 ^ _y];
}
}
else {
if (!ch[x][1 ^ _y]) ch[x][1 ^ _y] = rc();
if (_v) x = ch[x][1 ^ _y];
else {
tag[ch[x][1 ^ _y]] = true;
if (!ch[x][_y]) ch[x][_y] = rc();
x = ch[x][_y];
}
}
}
}
bool get(int u, int x, int d) {
if (d == -1 or !ch[x][0]) return true;
if (tag[ch[x][0]] or !get(u, ch[x][0], d - 1)) {
if (tag[ch[x][1]]) return false;
a[u] |= (1 << d); if (!ch[x][1]) return true;
if (get(u, ch[x][1], d - 1)) return true;
else return a[u] ^= (1 << d), false;
}
return true;
}
bool Solve(int u) { rg.clear();
if (!dfs(u)) return false;
idx = -1, rc();
for (int v : rg) ins(dis[v], l[v], 0), ins(dis[v], r[v], 1);
if (!get(u, 0, V)) return false;
for (int v : rg) a[v] = (a[u] ^ dis[v]);
return true;
}
int main() {
#ifndef DEBUG
freopen("stream.in", "r", stdin);
freopen("stream.out","w",stdout);
#endif
scanf("%d%d", & c, & T);
while (T--) {
scanf("%d%d", & n, & m); int u, v, w;
lep(i, 1, n) scanf("%d%d", l + i, r + i);
lep(i, 1, m) {
scanf("%d%d%d", & u, & v, & w);
e[u].push_back(v), g[u].push_back(w);
e[v].push_back(u), g[v].push_back(w);
}
bool nt = false;
lep(i, 1, n) if (!vis[i] and !Solve(i)) { nt = true; break; }
if (nt) puts("-1");
else { lep(i, 1, n) printf("%d ", a[i]); puts(""); }
lep(i, 1, n) e[i].clear(), g[i].clear(), dis[i] = vis[i] = a[i] = 0;
}
return 0;
}
right
场切,爽。
可以发现对于某些 '\(h'\) 字符,可能会在某个时刻被激活,然后每个时刻向左生产一个 \('s'\) 。
可以倒序处理出激活时间,然后我们就可以直接扫原序列,找到第一个 \(\ge x\) 的位置,然后分讨输出。
优化直接线段树二分维护这个式子。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
typedef long long ll;
typedef std::pair<ll, int> PLI;
struct node { int i, x, k; };
int c, T, n, q, tm[_]; char s[_], ans[_];
ll sum[_ << 2], tot[_ << 2];
std::vector<node> o[_];
std::vector <int> l[_];
void prt(ll res, int i, int x, int k, int q) {
if (res >= k) {
if (s[i] == 'i') {
if (tm[i - 1] and x >= tm[i - 1] - 1) ans[q] = 'e';
else ans[q] = 'i';
}
else if (s[i] == 's') {
if (i > 2 and tm[i - 2] == tm[i] + 2 and x >= tm[i - 2] - 1) ans[q] = 'r';
else ans[q] = 's';
}
else {
if (i > 2 and tm[i - 2] == tm[i] + 2 and x >= tm[i - 2] - 1 and
(tm[i] ? res - (x - tm[i] + 1) : res) == k) ans[q] = 'r';
else if (res == k) ans[q] = s[i];
else ans[q] = 's';
}
}
else ans[q] = '0';
}
#define ls p << 1
#define rs p << 1 | 1
#define md ((s + t) >> 1)
void mdy(int d, int x, int y, int s, int t, int p) {
tot[p] += x, sum[p] += y; if (s == t) return;
d <= md ? mdy(d, x, y, s, md, ls) : mdy(d, x, y, md + 1, t, rs);
}
PLI qry(ll x, int k, int s, int t, int p) {
if (s == t) return { (x + 1) * tot[p] + 1 - sum[p], s };
ll tmp = (x + 1) * tot[ls] - sum[ls] + (md - s + 1);
if (tmp >= k) return qry(x, k, s, md, ls);
auto nw = qry(x, k - tmp, md + 1, t, rs);
nw.first += tmp; return nw;
}
#undef ls
#undef rs
#undef md
void Mdy(int d, int x, int y) { mdy(d, x, y, 1, n, 1); }
PLI Qry(int x, int k) { return qry(x, k, 1, n, 1); }
int main() {
#ifndef DEBUG
freopen("right.in", "r", stdin);
freopen("right.out","w",stdout);
#endif
scanf("%d%d", & c, & T);
while (T--) {
scanf("%d%d%s", & n, & q, s + 1);
rep(i, n - 1, 1) {
if (s[i] == 'h') {
if (s[i + 1] == 'e') tm[i] = 1;
else if (s[i + 1] == 'i') {
if (i + 2 <= n and s[i + 2] == 's') tm[i] = 2;
else if (tm[i + 2]) tm[i] = tm[i + 2] + 2;
}
}
}
lep(i, 1, n) if (tm[i]) l[tm[i]].push_back(i);
int x, k;
lep(Q, 1, q) {
scanf("%d%d", & x, & k);
if (x > n) o[n + 1].push_back({Q, x, k});
else o[x].push_back({Q, x, k});
}
lep(i, 1, n + 1) {
for (int v : l[i]) Mdy(v, 1, tm[v]);
for (auto t : o[i]) {
auto nw = Qry(t.x, t.k);
prt(nw.first, nw.second, t.x, t.k, t.i);
}
l[i].clear(), o[i].clear();
}
lep(i, 1, q) putchar(ans[i]), putchar('\n');
lep(i, 1, n) tm[i] = 0;
lep(i, 1, 4 * n) sum[i] = tot[i] = 0;
}
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

Tire + 线段树二分 + Lucas/数位DP/ad-hoc
浙公网安备 33010602011771号