Codeforces 1586E. Moment of Bloom
思路
从原图构造任意生成树,在生成树上暴力即可,下证正确性。
首先证明,对于树,在操作后清除所有奇数边所需的次数是 s u m 2 \cfrac{sum}{2} 2sum ,其中 s u m sum sum 代表奇数节点个数 (注意,对于树来说,一对操作的路径是唯一的)
因为一次操作最多只能改变两个节点的奇偶性,全为偶数的必要条件为所有节点都为偶数,又由欧拉路径,总存在可行解,证毕
然后证明,任意原图的路径选择不会优于生成树上的路径选择
原图通过操作后,将得到的奇数边清除的次数下限亦为 s u m 2 \cfrac{sum}{2} 2sum ,原理同上
那么暴力的正确性证毕
代码
#include <bits/stdc++.h>
using namespace std;
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef pair<int, int> pii;
const int mod = 1e9+7;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n, m, qq;
vector<int> to[300300];
vector<int> ans[300300];
int sum[300300];
int up[300300];
int dep[300300];
void dfs(cint u, cint fa) {
dep[u] = dep[fa] + 1;
up[u] = fa;
for(int v: to[u]) {
if(v != fa && !dep[v]) {
dfs(v, u);
}
}
}
int ss = 0;
void sol(cint u, cint fa) {
if(sum[u]%2) ++ss;
for(int v: to[u]) {
if(v != fa && dep[v] == dep[u]+1) {
sol(v, u);
}
}
}
int main() {
//freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> m;
int u, v;
for(int i=1; i<=m; i++) {
cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u);
}
dfs(1, 1);
cin >> qq;
for(int i=1; i<=qq; i++) {
cin >> u >> v;
vector<int> tmp;
while(dep[v] > dep[u]) {
tmp.push_back(v);
v = up[v];
}
while(dep[u] > dep[v]) {
ans[i].push_back(u);
u = up[u];
}
while(u != v) {
ans[i].push_back(u);
tmp.push_back(v);
u = up[u];
v = up[v];
}
ans[i].push_back(u);
for(int j=tmp.size()-1; j>=0; j--) ans[i].push_back(tmp[j]);
for(int j=0; j<ans[i].size(); j++) {
if(j != 0 && j != ans[i].size()-1) ++sum[ans[i][j]];
++sum[ans[i][j]];
}
}
sol(1, 1);
// cout << ss << endl;
if(ss == 0) {
cout << "YES" << endl;
for(int i=1; i<=qq; i++) {
cout << ans[i].size() << endl;
for(int j: ans[i]) cout << j << ' ';
cout << endl;
}
}
else {
cout << "NO" << endl;
cout << ss/2 << endl;
}
return 0;
}