Codeforces Round #749 (Div. 1 + Div. 2, based on Technocup 2022 Elimination Round 1) E. Moment of Bloom

题意

给出(n)个点(m)条边的无向连通图(保证没有重边和自环),(q)次询问,每次询问给出(a,b)

初始时,所有的边边权均为0,对于每次询问,你必须选择一条从(a)(b)的简单路径,把路径上的每条边的边权+1。

回答是否可以做到(q)次询问之后图中每条边的边权均为偶数,如果可能,输出每次询问中选择的边。

若不可能,输出最少还要进行多少次操作,保证所需额外操作数不超过(10^{18}).

简单路径:每个点只能访问不超过一次。

(2le n le 3 imes 10^5, n-1 le m le min(frac{n(n-1)}{2}, 3 imes 10^5), 1le q le 3 imes 10^5, nqle 3 imes 10^5)

sol

这辈子想不出来的构造

注意到每次询问所选的路径边数和一定得是偶数(假设最终被选过的边共有x条,每条边都要经过偶数次,x个偶数之和仍为偶数)

若不可能,还需要进行的操作数不会超过q(把每个询问再做一次 必然合法)

在无向连通图上用具体路径判断是否有解做不了

猜想有不考虑具体路径进行判断的方法

注意到存在合法解的必要条件是每个点都被查询了偶数次(和该点相连的所有边都被盖了偶数次,所以该点得被询问偶数次)

考虑构造一个(n)个点(q)条边的新图,若某次询问为(a,b),则在新图的((a,b))​​​之间连一条边,如果有解,每个点的度数都将会是偶数,因此新图中的每个连通块中都存在欧拉回路。让每个欧拉回路的最后一条边对应的路径沿着欧拉回路上其他边对应的路径走回去,即可使得每条边都经过两次。

比如询问((1,2),(1,3),(2,3)),我们可以让前两条路径正常走((1,2),(1,3)),第三条路径拆成先沿((1,2))从2返回1,再沿((1,3))从1返回3. 这样能够使得每条路径经过偶数次。

因此每个点被查询了偶数次是有解的充要条件,先考虑构造解。

如果最终答案输出路径不能确定的话,很难搞,因此猜想能不能把它搞树上去

考虑树上的路径((u,v),(v,w))((u,w))。在经过路径((u,v))((v,w))后,路径((u,w))上的点都被经过了两次,路径((lca(u,v),lca(v,w))=(lca(u,w),v))​上的边也被经过了两次。类似地可以推出对于路径((a_1,a_2),(a_2,a_3),(a_3,a_4),cdots, (a_n, a_1))​,它们上的边也会被覆盖偶数次。也就是说,欧拉回路对应的若干路径可以使得树上的所有边被覆盖偶数次。

于是我们可以造个生成树,在树上找路径。

再考虑无解的情况,假如有(x)个点被询问了奇数次,要使得新图中所有点度数均为偶数,就需要使总度数增加(x)

如果(x)是偶数,直接输出(x/2)就行

如果(x)是奇数,由于新图中每次询问会使得总度数(+2), 若(x)是奇数则说明图中总度数为偶数+奇数个奇数的和=奇数,不符合题意,因此这种情况不存在。

写的时候可以不写lca输出路径,只要让他们每次深度更深的点往上爬即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 7;
int n, q, m, k, tot;
struct _ {
    int u, v;
}e[maxn];
struct que {
    int a, b;
}qq[maxn];
vector<int> adj[maxn];
int fa[maxn], deg[maxn], dep[maxn];
int get(int x) {
    return fa[x] == x ? x : fa[x] = get(fa[x]);
}
bool merge(int x, int y) {
    int fx = get(x), fy = get(y);
    if (fx ^ fy) {
        fa[fx] = fy;
        adj[x].push_back(y);
        adj[y].push_back(x);
        return true;
    }
    return false;
}
void dfs(int u) {
    for (int i = 0; i < adj[u].size(); i++) {
        int v = adj[u][i];
        if (!dep[v]) {
            fa[v] = u;
            dep[v] = dep[u] + 1;
            dfs(v);
        }
    }
}
vector<int> ansl, ansr;
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d", &e[i].u, &e[i].v);
    }
    scanf("%d", &q);
    for (int i = 1; i <= q; i++) {
        scanf("%d%d", &qq[i].a, &qq[i].b);
        deg[qq[i].a]++; deg[qq[i].b]++;
    }
    for (int i = 1; i <= n; i++) {
        k += (deg[i] & 1);
    }
    if (k) {
        printf("NO
%d
", k/2);
        return 0;
    }
    puts("YES");
    for (int i = 1; i <= n; i++) 
        fa[i] = i;
    int rst = n;
    for (int i = 1; i <= m; i++) {
        rst -= merge(e[i].u, e[i].v);
        if (rst == 1) 
            break;
    }
    fa[1] = dep[1] = 1; 
    dfs(1);
    for (int i = 1; i <= q; i++) {
        int u = qq[i].a, v = qq[i].b;
        ansl.clear();
        ansr.clear();
        while (u != v) {
            if (dep[u] < dep[v]) {
                ansr.push_back(v);
                v = fa[v];
            }
            else {
                ansl.push_back(u);
                u = fa[u];
            }
        }
        printf("%d
", ansl.size() + ansr.size() + 1);
        for (int j = 0; j < ansl.size(); j++) {
            printf("%d ", ansl[j]);
        }
        printf("%d ", u);
        for (int j = ansr.size()-1; j >= 0; j--) {
            printf("%d ", ansr[j]);
        }
        printf("
");
    }
}
原文地址:https://www.cnblogs.com/YjmStr/p/15430849.html