(奇偶性真是太能整活了)
找到第一句假话,显然在此之前需要找到一种方式存储先前给定的所有关系.
为什么会想到"关系"呢?观察每一行给出的三元有序组,前两者是两个数值,最后一者表示某种状态,即奇偶性.应该可以联想到,两个数值表达的对象具有着此状态所表示的关系.
也就是说,给定的数据是(对象A,对象B,A和B的关系),不要解读成了(数值,数值,一个未定的奇数/偶数),或者(对象,对象,一个未定的奇数/偶数),两个对象的某个共同数值有其他的处理方法(见借教室),而两个对象的关系使用并查集处理.在本题中,一个区间内所有1的数量即该区间所有数的和,表示区间数值的和使用前缀和数组:
如果区间[a,b]中有偶数个1,那么(前缀和数组)s[b]-s[a-1]为偶数,即s[b]与s[a-1]奇偶性相同.
如果区间[a,b]中有奇数个1,那么(前缀和数组)s[b]-s[a-1]为奇数,即s[b]与s[a-1]奇偶性不同.
接下来的事情就是用拓展域并查集处理了(也有人把这个叫种类并查集).观察数据范围,会想到离散化的.
实际上并不需要创建这个前缀和数组.因为并不需要对其进行任何操作,也没有可用的数据.注意区间[a,b]对应的对象是s[a-1]和s[b].
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <cstdio> #include <string> #include <iostream> #include <queue> using namespace std; struct S{ int l, r, v; }; deque<S> q; int n, m, ind, ans, fa[10010], dis[10010]; void discrete() { sort(dis + 1, dis + 1 + ind); ind = unique(dis + 1, dis + 1 + ind) - dis - 1; } int query(int x) { return lower_bound(dis + 1, dis + 1 + ind, x) - dis; } int get(int x){if(fa[x] == x) return x; return fa[x] = get(fa[x]);} void merge(int x, int y) {fa[get(y)] = fa[get(x)];} int main(){ cin >> n >> m; ans = m; for(int i = 1; i <= m; i++) { int a, b; string c; cin >> a >> b >> c; a--; q.push_back({a, b, c == "even" ? 1 : -1}); dis[++ind] = a, dis[++ind] = b; } discrete(); for(int i = 1; i <= ind * 2; i++) fa[i] = i; while(!q.empty()){ S cur = q.front(); q.pop_front(); int x = query(cur.l), y = query(cur.r), z = cur.v; if(z == -1){ // odd, diff if(get(x) == get(y)){ans = m - q.size() - 1; break;} merge(y, x + ind), merge(x , y + ind); }else{ // even, same if(get(x) == get(y + ind)){ans = m - q.size() - 1; break;} merge(x, y), merge(x + ind, y + ind); } } cout << ans << endl; return 0; }
玩奇偶性的还有这题.可以看出来奇偶性可以往前缀和上面想想.