AT2046 [AGC004F] Namori

https://www.luogu.com.cn/problem/AT2046

写这题的时候完全没有想清楚啊啊

重新看的时候盯了一个小时都没有看懂我在写啥

神仙思维分析题

首先考虑树的情况,可以把原图二分图染色一下,然后每次同色的取反就变成了异色的交换

原问题变为是否能把所有的黑白点位置交换

摸张官方题解的图

在这里插入图片描述

把给点记为\(1\),白点记为\(-1\),\(a[u]\)为子树和

显然\(a[rt]=0\),答案的下界为\(\sum |a[rt]|\)

不难证明下界一定能去到,由叶子结点向上归纳证明即可

奇环

不难发现,假设断开的那条边是\((S,T)\),二分图染色后\(S,T\)是同一种颜色,并且每次可以消掉/添加 两个

所以只需要以\(S\)为根,看\(|a[S]|\)是否为偶数即可,然后把环上所有点的\(a[u]-=a[S]/2\)

假设\(a[S]=2x\),如果那么环上\(a\)的变化就可以写成下面的形式

在这里插入图片描述

后面那条是因为\(|2x-x|=|x|\)所以可以统一的写成这种形式

偶环

这个就有点复杂了

首先还是可以二分图染色的,所以同样考虑断开\((S,T)\)这条边,以\(S\)为根

显然\(a[S]=0\)

同样考虑\(T->S\)传输了\(x\)个点,环上\(a\)的形式就可以用下图表示

在这里插入图片描述

同样的因为\(a[S]=0\)所以\(|x|=|0-x|\),可以统一写成下面那个形式

\(x\)通过零点分段法可以得到为环上\(a\)的中位数

代码不难实现,主要难度在思维

code:

#include<bits/stdc++.h>
#define ll long long
#define N 200050
using namespace std;
int fa[N], a[N], sta[N], top, n, m, S, T, b[N];
vector<int> g[N];
int get(int x) {
    return x == fa[x]? x : (fa[x] = get(fa[x]));
}
void merge(int x, int y) {
    x = get(x), y = get(y);
    if(x == y) return;
    fa[x] = y;
}
void dfs(int u, int fa, int o) {
    a[u] = o;
    for(int v : g[u]) {
        if(v == fa) continue;
        dfs(v, u, - o), a[u] += a[v];
    }
}
void find(int u, int fa) {
    if(sta[top] == T) return;
    sta[++ top] = u;
    for(int v : g[u]) {
        if(v == fa) continue;
        find(v, u);
    }
    if(sta[top] != T) top --;
}
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++) fa[i] = i;
    S = 1, T = 1;
    for(int i = 1; i <= m; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        if(get(u) == get(v)) S = u, T = v;
        else merge(u, v), g[u].push_back(v), g[v].push_back(u);
    }
    dfs(S, 0, 1);
    if(m == n - 1) {
        if(a[S]) {printf("-1"); return 0;}
    } else {
        find(S, 0);
        //for(int i = 1; i <= top; i ++) printf(" * %d * ", sta[i]); printf("\n");
        //for(int i = 1; i <= n; i ++) printf("%d ", a[i]); printf("\n");
        if(top & 1) {
            if(a[S] & 1) {printf("-1"); return 0;}
            int o = a[S] >> 1;
            for(int i = 1; i <= top; i ++) a[sta[i]] -= o;
        //    for(int i = 1; i <= n; i ++) printf("%d ", a[i]); printf("    %d\n", o);
        } else {
            if(a[S]) {printf("-1"); return 0;}
            for(int i = 1; i <= top; i ++) b[i] = a[sta[i]];
            sort(b + 1, b + 1 + top);
            for(int i = 1; i <= top; i ++) a[sta[i]] -= b[top / 2];
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; i ++) ans += abs(a[i]);
    printf("%lld", ans);
    return 0;   
}
posted @ 2022-02-22 20:00  lahlah  阅读(51)  评论(0编辑  收藏  举报