其实只是把别人的题解强行扩写了

写这篇题解之前我不会的预备知识:

欧拉通路:从图中一个点出发不重复地遍历所有边的路径(可以停在另一个点)

欧拉回路:从图中一个点出发不重复地遍历所有边的回路(必须回到出发点)

欧拉图:存在欧拉回路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数。

半欧拉图:存在欧拉通路的图。判断无向图为欧拉图的充要条件是所有点的度数均为偶数或只有两个点的度数为奇数。

一个图中如果存在度数为奇数的点,那么这样的点一定有偶数个。

从一些简单的例子考虑到复杂的例子。

  1. 给定的图是一个环,答案为0
  2. 给定的图是一个欧拉图,那么我们只需要把其中的边按照欧拉回路展开即可,答案为度数大于2的点的个数。因为最后的环中所有点的度数均为2,所以度数大于2的点必须被熔断,而且存在只熔断度数大于2的点的方案使得欧拉回路展开成一个环。

  例如左边”8”字形的图:从中间度数为4的点中间上下分开即可,得到右图,紫色节点为被熔断的节点形成的新节点。

  3.给定的图是一个连通图但不是欧拉图。那么这个图中一定包含偶数个奇度点。因此我们可以将奇度点两两“配对”,每一对点之间连一条“边”,这样就把原图变成了一个所有点度数均为偶的欧拉图,可以将其展开成一个环。这个地方比较难以理解,因为原题没有加边操作,那么怎么连“边”呢?关键在于:虽然我们认为先“加边”再“展开”,但真正操作时,需要先进行熔断操作,再进行连接操作,因为一个度数大于1的点无法进行连接操作。因此连“边”对应到题中的操作,是将两个奇度点进行熔断后再将它们各自产生的一个度数为1的节点连接起来。计算答案的时候,首先统计出原图中奇度点数目和偶度点数目,需要花费奇度点数/2的花费将图转化成欧拉图。再统计出“连边”后图中有多少点的度数大于2。只需用总的点数减去最后度数为2的点数。注意原先度数为1 的点“连边”后会变成度数为2的点。

接下来考虑多个连通块。

  4.给定的图不是一个连通图,但每个连通块都是一条链。首尾相接即可,答案=链数

  5.给定的图不是一个连通图,但每个连通块都是一个环。每个环熔断一次拆成链再首尾相接,答案=链数×2

  6. 给定的图不是一个连通图,每个连通块是一个欧拉图但不是环。我们是否需要先通过几次熔断把欧拉回路展开成环,然后再把环用一次熔断拆成链?不需要。事实上我们在把欧拉回路展开成环的过程中可以“顺手”把环拆成链。还是上面那个8字形的图,我们可以通过一步熔断完成“拆欧拉回路为环”和“拆环为链”。因此,如果一个欧拉回路不是环,那么在把它展开成环的过程中我们就可以完成“拆环为链”,不需要额外熔断一次。对于更加复杂的图,也是这样的。只有本来就是一个环的图才需要单独一次熔断“拆环为链”。

  7.多个连通块,每个连通块没有特殊性质(即原题)。

  我们希望将每个连通块都变成链,然后把每个链首尾相接即可。那么首先统计连接所有链的花费,把答案加上连通块数目。

  然后我们算出每个连通块拆成链的最小费用。实质上我们需要把每个连通块变成半欧拉图(有一条欧拉通路的图)再展开。将半欧拉图展开成一条链的花费和将欧拉图展开成一个环的花费可以相似地计算,等于度数大于2的点数。接下来按照奇度点的个数讨论。

  如果一个连通块的奇度点个数恰好为2,我们就可以直接计算展开成链的费用了。

  如果奇度点个数大于2,我们需要用之前把“一些相关联的熔断和连接操作”等效为“加边”的思路,将奇度点的个数减少到2,并统计“加边”所需的连接次数(熔断次数不在这里统计)。加完“边”,就可以统计展开成链的费用了。

  如果奇度点个数为0(是一个欧拉图),我们可以按照6计算这个连通块的费用。

然而考试写代码的时候并没有完全想清楚上面这些性质…..所以我写成了一大坨的分类讨论和手动枚举情况,还记录每个连通块中度数为1,2,3,4的点的个数,最后没有搞出“拆环为链”有时可以不用额外花费的性质,再加上一堆莫名其妙的细节错误,只有70...如果搞清楚上面这些性质,应该代码不会这样恶心。这个代码是考试代码基础上改的,不建议看,只是让读题解的你感受一下不想透性质就写代码会导致什么后果->_->

#include<cstdio>

const int maxn=,maxm=;

int u[maxm],v[maxm];

int ufs[maxn+maxm*];

int tot=;

int deg[maxn+maxm*];

int find(int x){

    return x==ufs[x]?x:ufs[x]=find(ufs[x]);

}

void link(int a,int b){

    ufs[find(a)]=find(b);

}

int odd[maxn+maxm*],even[maxn+maxm*],one[maxn+maxm*],two[maxn+maxm*],three[maxn+maxm*],four[maxn+maxm*];

int scc[maxn+maxm*];int cnt;

int maxdeg[maxn+maxm*];

int main(){

   // freopen("frame.in","r",stdin);

    int n,m;scanf("%d%d",&n,&m);

    tot=n;

    for(int i=;i<=m;++i){

        scanf("%d%d",u+i,v+i);

        if(u[i]==)u[i]=++tot;

        if(v[i]==)v[i]=++tot;

        deg[u[i]]++;deg[v[i]]++;

    }

    for(int i=;i<=tot;++i)ufs[i]=i;

    for(int i=;i<=m;++i){

        if(find(u[i])!=find(v[i]))link(u[i],v[i]);

    }

    for(int i=;i<=tot;++i){

        if(deg[i]!=){

            if(deg[i]&)odd[find(i)]++;

            else even[find(i)]++;

            if(deg[i]==)one[find(i)]++;

            else if(deg[i]==)two[find(i)]++;

            else if(deg[i]==)three[find(i)]++;

            else if(deg[i]==)four[find(i)]++;

        }

    }

    for(int i=;i<=tot;++i){

        if(ufs[i]==i&&deg[i]!=){

            scc[++cnt]=i;

        }

    }/*

    for(int i=1;i<=tot;++i){

        if(find(i)==scc[4])printf("%d\n",i);

    }*/

   // printf("%d\n",cnt);

    if(cnt==){

        int x=scc[];

        two[x]+=one[x];

        even[x]+=odd[x];

        int ans=odd[x]/+(even[x]-two[x]);

        printf("%d\n",ans);

    }else{

        int toteven=,tottwo=;

        int ans=,x;

        for(int i=;i<=cnt;++i){

            x=scc[i];//printf("%4d %4d %4d %4d %4d %4d ",odd[x],even[x],one[x],two[x],three[x],four[x]);

            if(odd[x]==){

                if(four[x]!=){

                    two[x]++;odd[x]=one[x]=;

                    four[x]--;

                }

                else if(two[x]!=){

                    two[x]--;even[x]--;

                    one[x]=odd[x]=;

                }else{

                    odd[x]=one[x]=;

                }

                ans++;

                tottwo+=two[x];

                toteven+=even[x];

            }else{//竟然还有可能拆偶点...

                if(one[x]>=){//不需要再拆1了

                    odd[x]-=;

                    one[x]-=;

                    two[x]+=one[x];

                    ans+=odd[x]/;

                }else if(one[x]==){//再拆一个1

                    if(three[x]!=){

                        three[x]--;ans++;

                        two[x]++;

                        odd[x]-=;

                        even[x]++;

                        ans+=odd[x]/;

                    }else{//这里是拆奇点,能否拆偶点?

                        odd[x]-=;

                        even[x]++;

                        ans++;

                        ans+=odd[x]/;

                    }

                }else if(one[x]==){//拆出两个1

                    int g1=0x7f7f7f7f,g2=0x7f7f7f7f,g3=0x7f7f7f7f,g4=0x7f7f7f7f,g5=0x7f7f7f7f;

                    if(three[x]>=){//拆两个3

                        g1=+(odd[x]-)/;

                    }

                    if(two[x]!=){

                        g2=+odd[x]/;

                    }

                    if(four[x]!=){

                        g3=odd[x]/-;

                    }

                    if(three[x]!=odd[x]&&three[x]!=){

                        g4=+(odd[x]-)/;

                    }

                    if(three[x]==){

                        g5=+(odd[x]-)/;

                    }

                    if(g1<=g2&&g1<=g3&&g1<=g4&&g1<=g5){

                        ans+=;

                        odd[x]-=;even[x]+=;two[x]+=;

                        ans+=(odd[x]/);

                    }else if(g2<=g1&&g2<=g3&&g2<=g4&&g2<=g5){

                        ans+=;

                        two[x]--;even[x]--;

                        ans+=odd[x]/;

                    }else if(g3<=g1&&g3<=g2&&g3<=g4&&g3<=g5){

                        two[x]++;//ans+=1;

                        ans+=odd[x]/;

                    }else if(g4<=g1&&g4<=g2&&g4<=g3&&g4<=g5){

                        two[x]++;even[x]+=;//ans+=2;

                        odd[x]-=;

                        ans+=odd[x]/;

                    }else{//printf("!");

                        even[x]+=;//ans+=2;

                        odd[x]-=;

                        ans+=odd[x]/;

                    }

                }

                tottwo+=two[x];

                toteven+=even[x]+odd[x];

            }//printf("ansnow %d\n",ans);

        }

        ans+=(toteven-tottwo);ans+=cnt;

       // printf("%d %d\n",toteven,tottwo);

        printf("%d\n",ans);

    }//while(1);

    return ;

}
 

bzoj2503&poj3387[NEERC2006]IdealFrame的更多相关文章

  1. 【BZOJ-2503】相框 并查集 + 分类讨论

    2503: 相框 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 71  Solved: 31[Submit][Status][Discuss] Desc ...

  2. [BZOJ2503][HAOI2006]均分数据

    BZOJ Luogu sol 如果已经确定了一个序列,现要求把这个序列分成m个连续段作为答案,那么就可以用一个显而易见的DP DP显然可以得到当前序列下的最优解. 所以模拟退火瞎JB改一改序列每次DP ...

  3. BZOJ.1312.[Neerc2006]Hard Life(分数规划 最大权闭合子图)

    BZOJ 最大密度子图. 二分答案\(x\),转为求是否存在方案满足:\(边数-x*点数\geq 0\). 选一条边就必须选两个点,所以可以转成最大权闭合子图.边有\(1\)的正权,点有\(x\)的负 ...

  4. Bzoj1312 / POJ3155 Neerc2006 Hard Life

    Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 459  Solved: 114 Description 在一家公司中,人事部经理与业务部经理不和.一次 ...

  5. bzoj2503 相框——思路

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2503 思路题: 首先,这种问题应该注意到答案只跟度数有关,跟其他什么连接方法之类的完全无关: ...

  6. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

随机推荐

  1. [转]oracle 实施方法论学习心得

      本周由部门新入职的同事为我们分享oracle实施方法论,本次重点围绕项目启动会来做介绍,并着重介绍了oracle与我们不一样的地方.收获颇丰,简单总结几个值得学习借鉴的要点: 1.      细节 ...

  2. <<你的灯亮着吗?>>读书笔记

    本书是美国计算机传奇人物杰拉尔德.温伯格和唐纳德.高斯所著,我在网上买到的2003年版的本书,发现本书用20则幽默的现代寓言故事,60幅精美插图,以及一系列的适当提问和建议,让我们的思考方式慢慢得以扩 ...

  3. PacificA中的租约与失效检测解读

    PacificA是微软的在基于log的分布式存储系统中的复制技术. 由于配置管理器维护着当前配置的真实情况,因此主节点不必保持不变. 这是因为配置的本地视图在不同服务器上是不必同步的. 特别是,我们必 ...

  4. Mybatis整合Spring

    根据官方的说法,在ibatis3,也就是Mybatis3问世之前,Spring3的开发工作就已经完成了,所以Spring3中还是没有对Mybatis3的支持.因此由Mybatis社区自己开发了一个My ...

  5. iOS视图弹出、平移、旋转、翻转、剪切等变换效果实现

    效果图: 1.定义属性 @property (nonatomic, strong) UIView *transformView;//发生变换的试图 @property (nonatomic, stro ...

  6. centos 进度条卡死

    CentOS 6.7 系统 在执行完删除更新包的全部操作之后, yum remove -y Deployment_Guide-en-US finger cups-libs cups ypbind &a ...

  7. Android 上实现像微信一样的用Fragment来实现的Tab切页效果 提供源码下载

    网有不少的例子,但是要么是像微信一样可是没有使用Fragment实现,要么是只实现了一个很简单的切换,没有下面的菜单页.这个例子有实现了,我觉得暂时够我用了##实现类:+ MainTabFragmen ...

  8. 使用GDB调试程序

    GDB(GNU Debuger)是Linux中的一个功能强大的调试器.GDB可以让你查看程序的内部结构,打印变量值,设置断点,单步调试源码,运行时修改变量值,路径跟踪,线程切换,显示汇编代码等. 编译 ...

  9. Windows共享作为公司文件服务器的案例

    1.目录结构 → 主管 部门 → 员工 → Public 2.实现效果 每个部门一个目录 部门主管可以访问自己和部门员工的目录 部门员工只可访问自己的目录 公共目录Public部门所有人都可访问 3. ...

  10. 用Lua扩展谷歌拼音输入法

    谷歌拼音输入法最后一次更新是2013年,最近2年毫无动静,这个产品应该已经停了,不过这并不影响对它的使用,我一直喜欢它的简洁和稳定. 说不上来什么原因,忽然想起了摆弄摆弄谷歌拼音输入法的扩展特性(我经 ...