第一反应是找到不变量。
注意到每一次操作会同时反转两个点的状态,所以点亮的点数量永远是偶数。
考虑每次反转确定一个点,但在图上很不好做。
所以随便找出一棵生成树来做,从叶子节点往上确定。
每次找出一个叶子节点,如果被染色就不要管了。
否则把它与他父亲的连边染色,在这个过程中实时统计有多少点被染色。
如果任何一个时间点有恰好 $k$ 个就输出方案,否则持续往上染色。
注意图可能不连通,还有要特判根节点没有父节点。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15, M = N << 1;
int n, m, k;
int h[N], e[M], w[M], ne[M], idx = 0;
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
vector< pair<int, int> > son[N];
bool st[N];
void dfs1(int u) {
st[u] = 1;
for (auto t : son[u]) {
int v = t.first, z = t.second;
if (st[v]) continue;
add(u, v, z), add(v, u, z);
// cout << '\t' << u << ' ' << v << ' ' << z << endl;
dfs1(v);
}
}
int fa[N], id[N], cnt[N];
void dfs2(int u, int father) {
fa[u] = father;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == father) continue;
id[v] = w[i]; cnt[u]++;
dfs2(v, u);
}
}
bool now[N];
int ans[N], tot = 0;
void print() {
puts("Yes");
printf("%d\n", tot);
for (int i = 1; i <= tot; i++) printf("%d ", ans[i]);
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1, u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
son[u].push_back(make_pair(v, i));
son[v].push_back(make_pair(u, i));
}
for (int i = 1; i <= n; i++) h[i] = -1, now[i] = 0;
for (int i = 1; i <= n; i++)
if (!st[i]) dfs1(i);
for (int i = 1; i <= n; i++)
if (!fa[i]) dfs2(i, 0);
queue<int> q;
for (int i = 1; i <= n; i++)
if (!cnt[i]) q.push(i);
int c = 0;
while (q.size()) {
if (c == k) {
print();
return 0;
}
int u = q.front(); q.pop();
if (fa[u] == 0) continue;
cnt[fa[u]]--;
if (cnt[fa[u]] == 0) q.push(fa[u]);
if (now[u]) continue;
ans[++tot] = id[u];
c -= now[u], c -= now[fa[u]];
now[u] ^= 1, now[fa[u]] ^= 1;
c += now[u], c += now[fa[u]];
}
puts("No");
return 0;
}