bzoj 3724: PA2014Final Krolestwo

4 篇文章 0 订阅
1 篇文章 0 订阅

题意:

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

题解:

假如没有长度为偶数这个限制,那么就新建一个点,将所有奇数点连起来,跑欧拉回路即可。
现在要求长度为偶数,可以将一个点拆成两个点, i , i + n i,i+n i,i+n,对于一条边,要么 x − > y + n x->y+n x>y+n,要么 y − > x + n y->x+n y>x+n,这样就能保证 x − > y x->y x>y的路径长度为偶数。
但是还要保证构造出来的图有欧拉回路,即每个点的度数为偶数。
首先找一棵生成树,非树边就随便连,对于树边 x − > f a x->fa x>fa假如x度数为偶数则连 x + n > f a x+n>fa x+n>fa否则连 f a + n − > x fa+n->x fa+n>x这样x和x+n的度数都一定是偶数。
但是根节点似乎不能得到保证,容易发现, 1 , n + 1 1,n+1 1,n+1奇偶性相同,假如同为奇,一定存在一条以 1 , n + 1 1,n+1 1,n+1为起点终点的欧拉路径。因为原图有偶数条边,而从左点集出发经过偶数条边一定会回到左点集,矛盾。所以 1 , n + 1 1,n+1 1,n+1必定同为偶。
对新图求欧拉回路即可。
code:

#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
vector<int> v[1000010],vc[1000010];
struct node{
    int x,y,next,c;
}e[1000010],a[1000010];int len=0,last[1000010];
int fa[1000010],d[1000010],cnt[1000010];
void ins(int x,int y,int c)
{
    a[++len].y=y;a[len].c=c;
    a[len].next=last[x];last[x]=len;
    cnt[x]++;
}
int findfa(int x) {return fa[x]==x?fa[x]:fa[x]=findfa(fa[x]);}
void bt(int x,int fa,int fr)
{
    for(int i=0;i<v[x].size();i++)
        if(v[x][i]!=fa) bt(v[x][i],x,vc[x][i]);
    if(x==1) return;
    if(cnt[x]&1) ins(x,fa+n,fr),ins(fa+n,x,fr);
    else ins(x+n,fa,fr),ins(fa,x+n,fr);
}
int sta[1000010],top=0;
bool vis[1000010];
void dfs(int x)
{
    for(int &i=last[x];i;i=a[i].next)
    {
        while(i&&vis[a[i].c]) i=a[i].next;
        if(!i) return;
        vis[a[i].c]=true;
        int k=i;dfs(a[i].y);
        sta[++top]=a[k].c;
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&e[i].x,&e[i].y);
        d[e[i].x]++;d[e[i].y]++;
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
        if(d[i]&1) ins(0,i,i+m),ins(i,0,i+m);
    for(int i=1;i<=m;i++)
    {
        int x=e[i].x,y=e[i].y,tx=findfa(x),ty=findfa(y);
        if(tx==ty) ins(x,y+n,i),ins(y+n,x,i);
        else
        {
            fa[tx]=ty;
            v[x].push_back(y);vc[x].push_back(i);
            v[y].push_back(x);vc[y].push_back(i);
        }
    }
    memset(vis,false,sizeof(vis));
    bt(1,0,0);dfs(0);
    //printf("ok\n");
    //for(int i=1;i<=top;i++) printf("%d ",sta[i]);printf("\n");
    while(top)
    {
        int ans1,ans2;
        ans1=sta[top--]-m;
        //printf("top:%d\n",top);
        int j=top;
        while(sta[j]<=m)
        {
            //printf("%d %d\n",j,sta[j]);
            j--;
        }
        ans2=sta[j]-m;
        //printf("j:%d\n",j);
        printf("%d %d %d\n",ans1,ans2,top-j);
        while(top>j) printf("%d ",sta[top--]);putchar('\n');
        top--;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值