• 【CodeForces 788B】奇妙的一笔画问题


    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=61845295

    题目大意
    给定n个点m条边的无向图,不同点之间只有一条边,但可以有自环,求本质不同的合法路径的总数
    一条合法路径是指经过m-2条边恰好2次,2条边1次
    两条路径不同指存在一条边在两种路径中经过的次数不同
    input
    第一行两个整数n,m
    接下来m行每行两个整数u,v,表示u到v有一条边
    output
    一个整数表示答案
    hint
    1<=n,m<=10^5,1<=u,v<=n

    第一眼看到这个题的时候,就有一点思路了。m-2条边经过两次,一条边经过两次,实质上就是一去一反。
    为什么会想到这个呢?想想小学的时候学一笔画问题,对于不能一笔画的图我们怎么强行的“一笔画”。就是把笔画过去,又画回来,这样一条边就经过了两次。嘛,这也算是一笔画问题的“性质”吧。

    回到问题来,我们希望去掉一团画了两次的路径,剩下的两条可以一笔画。两条边要能够一笔画,就必须要相邻。其相邻的两个点所连接的其他边结合“一去一反”的性质,可以证明在经过了2次后一定能回到出发点。

    然后就很容易啦。枚举每个点,从其连接的边中选择两条作为经过1次的边,就有cnt[i]*(cnt[i]-1)/2种选法,求和即可

    but。。。

    我只得了30分?! 立了一个flag,打脸打的啪啪响。为什么呢?
    1、注意题目:可能有自环
    自环就像是一个中转站,在经过这个点的时候,想沿着这个环走几次就走几次,显然一次也是可以的。那么,一条自环的边可以和任意一条边组合,就变成了去掉这个自环后,选一条边走一次,其他边走两次的问题了,在经过自环的点时顺便跑一下环,明显任何一条边都可以。
    所以还要统计自环对答案的贡献。
    2、图可能不连通
    但是要注意,如果只有单个的点孤立出来,这样的图仍然是可以的。我们所谓的联通是指边在一个联通图中。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    
    const int N=100000+5;
    
    ll deg[N],cir[N],cntc=0,edge[N][2],fa[N];
    int n,m;
    ll tot=0,hasc=0;
    ll head[N],end[2*N],nxt[2*N],hh=0;
    bool has[N];
    
    int getfa(int x){
        if(fa[x]==x) return x;
        return fa[x]=getfa(fa[x]);
    }
    int main(){
        scanf("%d%d",&n,&m);
        int u,v;
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            deg[u]++,deg[v]++;
            edge[i][0]=u,edge[i][1]=v;
            int fau=getfa(u),fav=getfa(v);
            if(fau!=fav) fa[fau]=fav;
            if(u==v) cir[++cntc]=u,deg[u]--;
        }
        for(int i=1;i<=m;i++){
            u=edge[i][0];
            int j;
            for(j=1;j<=m&&j==i;j++);
            v=edge[j][0];
            if(getfa(u)!=getfa(v)){
                printf("0");
                return 0;
            }
            v=edge[j][1];
            if(getfa(u)!=getfa(v)){
                printf("0");
                return 0;
            }
        }
        for(int i=1;i<=n;i++)
            tot+=deg[i]*(deg[i]-1)/2;
        for(int i=1;i<=cntc;i++){
            tot+=(m-deg[cir[i]]);
            if(has[cir[i]]==0){
                hasc++;
                has[cir[i]]=1;
            }
        }
        tot-=(hasc-1)*hasc/2;
        printf("%lld",tot);
        return 0;
    }

    总结:
    1、从题目出发思考问题,不要想着去套算法。生活经验或是以前学过的知识可能是可以触类旁通的。
    2、思考问题要思考全面。题目中提示了一些特殊点,要去思考其可能的情况。

  • 相关阅读:
    如何实现清浮动(转载)
    js动态删除某一行,内容超出单元格后超出的部分用省略号代替
    jquery页面隐藏和展开之间切换
    比较jquery中的after(),append(),appendTo()方法
    如何使用git管理代码
    网页游戏常见外挂原理及防御
    JQuery实现页面刷新后菜单保留鼠标点击addclass的样式
    【查询】—Entity Framework实例详解
    SQL Server清除连接过的服务器名称列表
    WebBrowser.ExecWB的完整说明
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763138.html
Copyright © 2020-2023  润新知