P6185 [NOI Online #1 提高组] 序列 题解

一、题目:

洛谷原题

二、思路:

这道题是一道蓝题,我都没想出来😥。我真的是菜到家了,今年的 NOI 不打铁都奇怪。

首先可以发现,操作 2 具有可传递性。若 \((a,b)\) 可以有操作 2,\((b,c)\) 也可以有操作 2,那么 \((a,c)\) 也可以等价地相当于有操作 2。所以我们可以将操作 1 和 2 看做两种无向边,使用并查集将第二种边的连通块缩成一个点。缩完点之后的图就只剩了第一种边。(注意,缩完点后的图是有自环的。)

设缩完点后的点权 \(\sum\limits b_x-a_x\)

又可以发现,若第一种边形成了一个奇环,那么就可以在任意两点同时 +1 或 -1,也可以在任意两点一加一减。所以,如果环的点权之和为偶数,就可以完成目标;若第二种边形成了一个偶环,则可以看成是二分图。二分图的左部点的点权和和右部点的点权和之差是个定值,所以如果两部的点权和相等,就可以完成目标。(特别地,自环是奇环,所以自环并不影响正确性。)

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int MAXN = 1e5 + 5;

int n, m, a[MAXN], b[MAXN], fa[MAXN], idx, c[MAXN];
long long sum[MAXN], sumc[3];
pair<int, int>optb[MAXN];
vector<int>linker[MAXN];

int find(int x) {
    if (x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

inline void merge(int x, int y) {
    x = find(x); y = find(y);
    if (x != y) fa[x] = y; 
}

bool dfs(int x, int col) {
    c[x] = col;
    bool ok = true;
    sumc[col] += sum[x];
    for (auto &y : linker[x]) {
        if (c[y] == col) ok = false;
        if (!c[y] && !dfs(y, 3 - col)) ok = false;  
    }
    return ok;
}

int main() {
    int T = read();
    while (T --) {
        
        n = read(); m = read();

        for (int i = 1; i <= n; ++ i) sum[i] = sumc[i] = c[i] = 0, linker[i].clear(), fa[i] = i;
        idx = 0;

        for (int i = 1; i <= n; ++ i) a[i] = read();
        for (int i = 1; i <= n; ++ i) b[i] = read();

        for (int i = 1; i <= m; ++ i) {
            int t = read(), x = read(), y = read();
            if (t == 2) merge(x, y);
            else optb[++ idx] = { x, y };
        } 

        for (int x = 1; x <= n; ++ x) {
            sum[find(x)] += b[x] - a[x];
        }

        for (int i = 1; i <= idx; ++ i) {
            int x = optb[i].first, y = optb[i].second;
            linker[find(x)].push_back(find(y));
            linker[find(y)].push_back(find(x));
        }

        bool flag = true;
        for (int x = 1; x <= n; ++ x) {
            if (!c[x] && find(x) == x) {
                sumc[1] = sumc[2] = 0;
                bool ok = dfs(x, 1);
                if (ok) {
                    if (sumc[1] != sumc[2]) { flag = false; break; }
                }
                else {
                    if ((sumc[1] + sumc[2]) & 1) { flag = false; break; }
                }
            }
        }
        if (flag) puts("YES");
        else puts("NO");
        
    }
    return 0;
}

posted @ 2021-07-06 19:44  蓝田日暖玉生烟  阅读(71)  评论(0编辑  收藏  举报