P5937 题解
前言
提供一种不用并查集的,非常好想的做法。
思路
题目就是说给定一堆 \(\sum\limits_{i=l}^r a_i \equiv w\pmod{2}\),判断哪个等式不合法。
这个形式明显可以前缀和优化:\(s_r - s_{l-1} \equiv w \pmod{2}\),其中 \(s_x = \sum\limits_{i=1}^x\)。
发现这玩意类似差分约束。于是考虑建图。
对于上面的形式,连边 \((l-1) \xrightarrow{\large \ w\ } r\)。
很自然地想到,如果当前查询的两个点 \((l - 1, r)\) 已经连通,他们可以直接计算出答案 \(ans\):走正向边 \(ans + w\),走反向边 \(ans - w\)。
进一步地, 由于 \(ans\) 与 \(w\) 都是 \(0\) 或 \(1\),不用考虑加减。直接连双向边,答案就是两点的距离。
最后就是如何计算两点的距离了:
- 可以直接 01bfs。
- 这个图在 \((u, v)\) 已经连通时不会再连边,说明它是一棵树。直接 dfs 即可。
记得离散化。时间复杂度 \(O(m^2)\),由于不可能建满边,所以跑得比预期快很多。
另外的
由于是一个树,应该可以倍增 lca 来实现 \(O(\log m)\) 加边与查询。
面前的大佬可以尝试写一下。
代码
#include <iostream>
#include <cstdio>
#include <unordered_map>
#include <algorithm>
using namespace std;
const int N = 10005;
struct Edge {int now, nxt, w;} e[N << 1];
int head[N], cur;
void add(int u, int v, int w)
{
e[++cur].now = v, e[cur].nxt = head[u], e[cur].w = w;
head[u] = cur;
}
struct Input {int l, r; string op;} input[N];
int tt[N << 1], tot;
int read()
{
int x;
cin >> x;
tt[++tot] = x;
return x;
}
int fin(int x) {return lower_bound(tt + 1, tt + tot + 1, x) - tt;}
bool vis[N];
int dis[N], emm[N];
void dfs(int u, int fa, int tag)
{
emm[u] = tag;
for (int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].now;
if (v == fa) continue;
dis[v] = (dis[u] + e[i].w) % 2, dfs(v, u, tag);
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) input[i].l = read(), input[i].r = read(), cin >> input[i].op;
sort(tt + 1, tt + tot + 1), tot = unique(tt + 1, tt + tot + 1) - tt - 1;
for (int i = 1; i <= m; i++) input[i].l = fin(input[i].l - 1), input[i].r = fin(input[i].r);
for (int i = 1; i <= m; i++)
{
int u = input[i].l, v = input[i].r, w = (input[i].op == "even" ? 0 : 1);
dis[u] = 0, dfs(u, 0, i);
if (emm[v] == i) //联通
{
if (dis[v] != w) return cout << i - 1, 0;
}
else add(u, v, w), add(v, u, w), vis[u] = true, vis[v] = true;
}
cout << m;
return 0;
}
希望能帮助到大家!