• ABC155F


    简述题意 给你N个数对 表示坐标与状态(0/1), M个操作,给定一个区间,区间内的坐标的状态翻转

    思路:看到区间修改,很容易想到差分,对数对sort,每个a_i与a_i-1异或构造差分数组b,每次对[l,r]区间操作时,只需要将b[l]与b[r+1]对1异或操作就行了

    那么我们如何判断哪些操作需要选择呢,我们可以将每一段有关联的区间操作连边建树,我们知道,当某个点为1时,要异或为0必须操作奇数次,统计每一组操作中差分数组为1的情况,如果有偶数个差分数组为1的情况,那么一定能使每个点变为0,因为每次操作能且只能操作2个点,2个点配对为一组

    那我们如何建呢,用并查集建一个生成树,统计每一棵树上的个数到树根上,这样判断树根就可以判断是否有解

    如果有解,那我们从每一个树根出发跑dfs,对每个点,如果其子树(包括自己)的差分数组为1的个数为奇数,那么该点就必须进行一次操作,将他与他的父节点转变一次,使得其子树的差分数组为1的个数为偶数,那么必定通过操作将每个点化为0,有点类似点分治找重心的过程,使用后序操作,保证每一个点最后都是0,因为每个点的siz为奇数的话,其子树节点已经全为0,他自身就需要改变,siz为偶数的话,他就是一个与其他奇数节点配对的点,不需要改变,或者说他是配对的1,也就是不需要操作,举个小例子,1-1 0-1,第一个的前驱不需要改变,第二个的需要

    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) ((x)&(-x))
    typedef pair<int, int> pii;
    typedef long long LL;
    
    const int maxm = 1e5+5;
    
    vector<pii> G[maxm];
    vector<int> ans;
    pii a[maxm];
    int b[maxm], pos[maxm], cnt[maxm], siz[maxm], fa[maxm];
    
    int Find(int x) {
        return fa[x] == x?x:fa[x] = Find(fa[x]);
    }
    
    void dfs(int u, int fa, int id) {
        siz[u] = b[u];
        for(auto i : G[u]) {
            int v = i.first;
            if(v == fa) continue;
            dfs(v, u, i.second);
            siz[u] += siz[v];
        }
        if(siz[u] & 1) ans.push_back(id);
    }
    
    void run_case() {
        int n, m;
        cin >> n >> m;
        for(int i = 1; i <= n; ++i) cin >> a[i].first >> a[i].second;
        sort(a+1, a+1+n);
        for(int i = 1; i <= n; ++i) pos[i] = a[i].first;
        for(int i = 1; i <= n+1; ++i) {
            fa[i] = i;
            b[i] = a[i].second ^ a[i-1].second;
        }
        for(int i = 1; i <= m; ++i) {
            int l, r, tl, tr;
            cin >> l >> r;
            l = lower_bound(pos+1, pos+1+n, l) - pos;
            r = upper_bound(pos+1, pos+1+n, r) - pos;
            tl = Find(l), tr = Find(r);
            if(tl == tr) continue;
            fa[tl] = tr;
            G[l].push_back(make_pair(r, i));
            G[r].push_back(make_pair(l, i));
        }
        for(int i = 1; i <= n+1; ++i) if(b[i]) cnt[Find(i)]++;
        for(int i = 1; i <= n+1; ++i)
            if(Find(i) == i && (cnt[i]&1)) {
                cout << "-1";
                return;
            }
        for(int i = 1; i <= n+1; ++i) {
            if(Find(i) == i) dfs(i, 0, 0);
        }
        sort(ans.begin(), ans.end());
        cout << ans.size() << "
    ";
        for(auto i : ans) cout << i << " ";
    }
    
    int main() {
        ios::sync_with_stdio(false), cin.tie(0);
        //cout.setf(ios_base::showpoint);cout.precision(8);
        run_case();
        cout.flush();
        return 0;
    }
    View Code
  • 相关阅读:
    伯努利数学习笔记
    贝尔数学习笔记
    LuoguP5075 [JSOI2012]分零食
    LuoguP5748 集合划分计数
    LuoguP3338 [ZJOI2014]力
    LuoguP5488 差分与前缀和
    BZOJ4833 [Lydsy1704月赛]最小公倍佩尔数
    FFT&NTT学习笔记
    csp2019游记
    与图论的邂逅09:树上启发式合并
  • 原文地址:https://www.cnblogs.com/GRedComeT/p/12323552.html
Copyright © 2020-2023  润新知