P5937 [CEOI1999]Parity Game

 (奇偶性真是太能整活了)

找到第一句假话,显然在此之前需要找到一种方式存储先前给定的所有关系.

为什么会想到"关系"呢?观察每一行给出的三元有序组,前两者是两个数值,最后一者表示某种状态,即奇偶性.应该可以联想到,两个数值表达的对象具有着此状态所表示的关系.

也就是说,给定的数据是(对象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].

#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;
}
P5937

玩奇偶性的还有这题.可以看出来奇偶性可以往前缀和上面想想.

原文地址:https://www.cnblogs.com/Gaomez/p/14409657.html