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