首先可以按 \(a_i\) 排序,对于区间 \([l_i, r_i]\) 可以通过二分确定实际影响到的 \(b_i\) 的区间。
考虑到区间 \(i\in [l, r]\) 的 \(b_i\) 都取反影响到的元素过多,考虑去减少影响到的元素。
于是可以想到令 \(c_i = b_i \oplus b_{i - 1}\),那么对于区间 \([l, r]\) 操作实际影响到的就只会有 \(c_l, c_{r + 1}\) 了。
此时相当于是 \(l, r + 1\) 可以共同取反,于是可以考虑连边 \((l, r + 1)\)。
此时就相当于是给定了一个图和 \(c_{1\sim n + 1}\),每次可以将一条边的两个端点的 \(c_i\) 取反,要将 \(c_{1\sim n + 1}\) 调整至目标状态全 \(0\)(因为有 \(b_0 = 0\),可以递推得到 \(b_{1\sim n + 1} = 0\))。
显然对于这个图可以分成不同的连通块,因为每个连通块间互相独立。
但是对于一张图来说,边太多也太杂,不好处理,所以继续考虑减少无用边。
考虑对于这个图跑出来一个生成树,那么可以发现操作不在生成树上的边可以通过操作树上两个端点之间路径上的所有边代替。
那么这个图就被缩减成一棵树了。
接下来考虑对这棵树来操作。
这部分是简单的,考虑随意钦定一个根,从叶子节点往上递推,这是因为此时连向儿子的边都已经确定了,只剩与父亲的连边还可以进行调整。
如果操作完后根的点权为 \(1\),因为根没有可以进行调整的边,一定无解。
若有解,上面的过程就已经确定了要操作的边了。
时间复杂度 \(\mathcal{O}((n + m)\log n)\)。
#include<bits/stdc++.h>
#define fi first
#define se second
const int maxn = 2e5 + 10;
using pii = std::pair<int, int>;
pii a[maxn];
int pre[maxn];
std::vector<pii> to[maxn];
int vis[maxn], ans[maxn];
void dfs(int u, int fa = 0, int fl = 0) {
vis[u] = 1;
for (auto [v, id] : to[u]) {
if (vis[v]) continue;
dfs(v, u, id);
}
if (pre[u] && ! fa) {
puts("-1");
exit(0);
}
ans[fl] = pre[u], pre[fa] ^= pre[u];
}
int main() {
int n, m; scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d%d", &a[i].fi, &a[i].se);
std::sort(a + 1, a + n + 1);
a[n + 1].fi = 2e9;
for (int i = 1; i <= n + 1; i++)
pre[i] = a[i - 1].se ^ a[i].se;
for (int i = 1; i <= m; i++) {
int l, r; scanf("%d%d", &l, &r);
l = std::lower_bound(a + 1, a + n + 2, pii{l, 0}) - a;
r = std::lower_bound(a + 1, a + n + 2, pii{r + 1, 0}) - a;
to[l].emplace_back(r, i), to[r].emplace_back(l, i);
}
for (int i = 1; i <= n + 1; i++)
if (! vis[i])
dfs(i);
std::vector<int> ANS;
for (int i = 1; i <= m; i++)
if (ans[i])
ANS.push_back(i);
printf("%zu\n", ANS.size());
for (int x : ANS)
printf("%d ", x);
return 0;
}
标签:pre,Atcoder,int,可以,Solution,取反,maxn,ABC155F,考虑
From: https://www.cnblogs.com/rizynvu/p/18357682