BZOJ3724: PA2014Final Krolestwo

11 篇文章 0 订阅
11 篇文章 0 订阅

跪dalao……

我们新建一个点0,连边向所有奇数度点,于是得到了一个偶数条边,所有点度数都是偶数的图
这个图存在欧拉回路,根据每次流出0,流入0的两个奇数点可以配对
但是这题要求路径长度为偶数,于是我们将n个点拆成2n个点,这样配对的奇数点之间的路径长度就是偶数了
我们现在想在2n个点中建入原图中的边,使所有点度数都为偶数
考虑怎么连原图中的边
0的度数显然为偶数
建出原图的生成树,对于不在生成树上的边,在二分图上随便建
然后对于生成树上的边 < fa,x>,
如果x的度数是奇数,连边< x,fa+n>
如果x的度数是偶数,连边< x+n,fa>
这样做能保证x的度数是偶数,因为拆点前x的度数是偶数,所以x+n的度数一定是偶数
于是我们能保证除了树根的两个点root,root+n,所有点的度数都是偶数
因为所有点的度数和是偶数, droot,droot+n 一定同奇偶
若他们都为奇数,则存在一条root到root+n的欧拉回路(好吧不能叫回?
因为原图的总边数是偶数,根据二分图性质,从root走完所有边一定在root这边,不合法
所以这种情况不存在
于是所有点度数都是偶数

跑个欧拉回路就好了qaq

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 1010000;
const int maxm = 2010000;

int n,m;
int d[maxn],cnt[maxn],e[maxm][2];
struct edge{int y,c,nex;}a[maxm]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c)
{
    a[++len]=(edge){y,c,fir[x]};fir[x]=len;
    a[++len]=(edge){x,c,fir[y]};fir[y]=len;
    cnt[x]++,cnt[y]++;
}

int fa[maxn];
int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}

vector<int>V[maxn],vi[maxn];
void build(const int x,const int fa,const int fai)
{
    for(int i=0;i<V[x].size();i++) if(V[x][i]!=fa)
        build(V[x][i],x,vi[x][i]);
    if(x==1) return;
    if(cnt[x]&1) ins(x,fa+n,fai);
    else ins(x+n,fa,fai);
}
bool use[maxm];
int t[maxm],tp;
void dfs(const int x)
{
    for(int k=fir[x];k;k=fir[x]) 
    {
        while(k&&use[a[k].c]) k=a[k].nex;
        if(!k) return;
        use[a[k].c]=true;
        fir[x]=a[k].nex;
        dfs(a[k].y),t[++tp]=a[k].c;
    }
}

int main()
{
    read(n); read(m);
    for(int i=1;i<=m;i++)
    {
        int x,y; read(x); read(y);
        d[x]++,d[y]++;
        e[i][0]=x,e[i][1]=y;
    }
    for(int i=1;i<=n;i++) if(d[i]&1)
        ins(0,i,m+i);
    for(int i=1;i<=n;i++) fa[i]=i;

    for(int i=1;i<=m;i++)
    {
        int x=e[i][0],y=e[i][1];
        if(findfa(x)==findfa(y)) ins(x,y+n,i);
        else
        {
            fa[findfa(x)]=findfa(y);
            V[x].push_back(y),vi[x].push_back(i);
            V[y].push_back(x),vi[y].push_back(i);
        }
    }
    build(1,0,0);
    dfs(0);
    while(tp>0)
    {
        int ans1,ans2,ans3;
        ans1=t[tp--]-m;
        int j=tp;while(t[j]<=m) j--;
        ans2=t[j]-m;
        printf("%d %d %d\n",ans1,ans2,tp-j);
        while(tp>j) printf("%d ",t[tp--]); putchar('\n');
        tp--;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值