码迷,mamicode.com
首页 > 其他好文 > 详细

@bzoj - 3724@ PA2014Final Krolestwo

时间:2020-01-25 00:59:46      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:esc   tle   tin   二分   roles   选择   str   路径   连通   


@description@

你有一个无向连通图,边的总数为偶数。
设图中有k个奇点(度数为奇数的点),你需要把它们配成k/2个点对(显然k被2整除)。对于每个点对(u,v),你需要用一条长度为偶数(假设每条边长度为1)的路径将u和v连接。每条路径允许经过重复的点,但不允许经过重复的边。这k/2条路径之间也不能有重复的边。

原题链接。

@solution@

先不考虑路径长度必须为偶数,其实就是跑 k/2 条欧拉路径。
这个实现方法很多,讲一种这道题可以用的:建虚点 s,s 向所有奇点连虚边,跑欧拉回路,然后断开所有虚边。

考虑路径长度为偶数,联想到二分图。考虑一个点拆成黑白两点,原来的一条边 (u, v) 对应了 u 的黑/白点连向 v 的白/黑点。
如果虚点 s 只向奇点的黑点连边,则跑出来的欧拉回路一定是路径长度为偶数。

不过这个拆点中,一条边 (u, v) 只能对应 u 的黑/白点连向 v 的白/黑点中一种情况。
所以我们要加以选择,使得建出来的新图中每个点的度数依然为偶数。
因为白点度数 + 黑点度数 = 原图点度数,所以我们只需要白点度数为偶数即可。

考虑将问题转化一下:给每条边 (u, v) 定向成 u -> v 或 v -> u,定向后每个点的出边即这个点白点连出去的边。
这个问题就是经典问题了,建出任意一棵生成树(一般选 dfs 树)后,非树边随便选,树边自下而上调整,即可保证每个白点的度数为偶数。

@accepted code@

#include <cstdio>

const int MAXN = 2*250000;

struct Graph{
    struct edge{
        int to, id; bool tag;
        edge *nxt, *rev;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
    Graph() {ecnt = edges;}
    void addedge(int u, int v, int i) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->id = i, p->nxt = adj[u], adj[u] = p;
        q->to = u, q->id = i, q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
//      printf("! %d %d %d\n", u, v, i);
    }
}G1, G2;
#define rep(G, x) for(Graph::edge *p=G.adj[x];p;p=p->nxt)

int dfn[MAXN + 5], oud[MAXN + 5], dcnt;
void dfs(int x, int f) {
    dfn[x] = (++dcnt);
    rep(G1, x) {
        if( p->to == f ) continue;
        if( dfn[p->to] ) {
            if( dfn[p->to] < dfn[x] )
                G2.addedge(2*x, 2*p->to-1, p->id), oud[x]++;
        }
        else {
            dfs(p->to, x);
            if( oud[p->to] & 1 )
                G2.addedge(2*p->to, 2*x-1, p->id), oud[p->to]++;
            else G2.addedge(2*p->to-1, 2*x, p->id), oud[x]++;
        }
    }
}

Graph::edge *stk[MAXN + 5]; int tp;

void dfs2(int x) {
//  printf("! %d\n", x);
    for(;G2.adj[x];) {
        Graph::edge *p = G2.adj[x]; G2.adj[x] = G2.adj[x]->nxt;
        if( p->tag ) continue;
        p->tag = p->rev->tag = true;
        dfs2(p->to);
        stk[++tp] = p->rev;
    }
}

int deg[MAXN + 5];

int main() {
    int n, m; scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++) {
        int a, b; scanf("%d%d", &a, &b);
        G1.addedge(a, b, i), deg[a]++, deg[b]++;
    }
    for(int i=1;i<=n;i++)
        if( deg[i] & 1 ) G2.addedge(0, 2*i-1, -1);
    dfs(1, -1), dfs2(0);
    int lst = 1;
/*
    for(int i=1;i<=tp;i++)
        printf("%d ", stk[i]->id);
    puts("");
*/
    for(int i=2;i<=tp;i++) {
        if( stk[i]->id == -1 ) {
            printf("%d %d %d\n", (stk[lst]->to + 1)/2, (stk[i]->rev->to + 1)/2, i - lst - 1);
            for(int j=lst+1;j<=i-1;j++) {
                printf("%d%c", stk[j]->id, j + 1 == i ? '\n' : ' ');
            }
            i++, lst = i;
        }
    }
}

@details@

需要注意欧拉回路的实现,要用类当前弧优化的方法进行优化,不然时间复杂度不正确。

@bzoj - 3724@ PA2014Final Krolestwo

标签:esc   tle   tin   二分   roles   选择   str   路径   连通   

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12232698.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!