• hdu2121-Ice_cream’s world II


    给出一个有向图,求最小树形图和它的最小的根。

    分析

    这个题又写了一晚上~我之前的朱刘算法写法是我乱想的,只有那道题可以过……所以去找了一份代码来看,发现我的写法超级麻烦啊,所以就学习了一下那种写法,非常漂亮,但是有一些判断要考虑。

    这是一个不定根问题,经典解决方法是添加一个超级根,向每个点连一条长度大于所有边权和的边,设它为(sum),然后以超级根为根求最小树形图。如果最后最小树形图的答案超过了(2sum),那么就说明超级根至少连出去两条边,即原图不连通。如果原图连通,那么把(sum)减掉即可。

    关键在于如何求最小的根。可以发现,如果我们从小到大把超级根到所有点的边加进去,然后在算法过程中从小到大遍历边,如果有多个(in)最小且从root过来的,那么我们会找到最小的那个。但是缩点怎么办呢?朱刘算法的这种写法的一个很大的好处就是保留了所有的点,只是改了它们的id,所以我们可以以边为关键搜索,如果找到第(i)条边为((root,v,w)),它是最小的,那么就把最小根更新为(i),那么最终的最小根就是(i-m),其中(m)为原图上的边数。非常巧妙!

    代码

    为什么全改giant还比只改用到的快200ms啊。

    代码的几个需要注意的地方:

    • 找环的时候不能把已经有id的点再给一次id
    • 在内部找环的时候从上一个开始找
    • 只有当原来的边的两个点不在同一个环中才能减它的边权
    • 前面在处理in的时候当(u=v)的时候直接跳过这条边
    • 现在这个写法中的无环条件为col=1
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long giant;
    giant read() {
        giant x=0,f=1;
        char c=getchar();
        for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
        for (;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const giant maxn=2e3+10;
    const giant maxm=4e4+10;
    const giant inf=1e8+7;
    struct edge {
        giant u,v,w,nxt;
    };
    struct graph {
        edge e[maxm];
        giant h[maxn],tot,in[maxn],from[maxn],id[maxn],tic[maxn],n,real,vis[maxn];
        void clear(giant _n) {
            memset(h,0,sizeof h),tot=0,n=_n;
        }
        void add(giant u,giant v,giant w) {
            e[++tot]=(edge){u,v,w,h[u]};
            h[u]=tot;
        }
        giant MTG(giant root) {
            giant ret=0;
            while (true) {
                fill(in+1,in+n+1,inf);
                memset(id,0,sizeof id);
                memset(from,0,sizeof from);
                memset(vis,0,sizeof vis);
                for (giant i=1;i<=tot;++i) if (e[i].u!=e[i].v && in[e[i].v]>e[i].w) {
                    in[e[i].v]=e[i].w,from[e[i].v]=e[i].u;
                    if (e[i].u==root) real=i; 
                }
                giant col=1,j;
                for (giant i=1;i<=n;++i) if (i!=root) {
                    ret+=in[i];
                    for (j=i;j!=root && !id[j];j=from[j]) if (vis[j]!=i) vis[j]=i; else break;
                    if (j!=root && !id[j]) {
                        for (giant k=from[j];k!=j;k=from[k]) id[k]=col;
                        id[j]=col++;
                    }
                }
                if (col==1) break;
                for (giant i=1;i<=n;++i) if (!id[i]) id[i]=col++;
                for (giant i=1;i<=tot;++i) {
                    giant u=e[i].u,v=e[i].v;
                    e[i].u=id[u],e[i].v=id[v];
                    if (e[i].u!=e[i].v) e[i].w-=in[v];
                }
                root=id[root],n=col-1;
            }
            return ret;
        }
    } A;
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("test.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
        giant n,m;
        while (~scanf("%lld%lld",&n,&m)) {
            A.clear(n+1);
            giant sum=1;
            for (giant i=1;i<=m;++i) {
                giant u=read()+1,v=read()+1,w=read();
                A.add(u,v,w);
                sum+=w;
            }
            for (giant i=1;i<=n;++i) A.add(n+1,i,sum);
            giant ans=A.MTG(n+1);
            if ((ans-=sum)>=sum) puts("impossible
    "); else 
            printf("%lld %lld
    
    ",ans,A.real-m-1);
        }
    }
    
  • 相关阅读:
    差分约束
    关系运算图。。。(差分约束)
    克鲁斯卡尔算法+并查集
    整体代换(数学)
    魔性の分块 | | jzoj1243 | | 线段树の暴力
    论人品 | | noip1015模拟考
    hash 表 | | jzoj 1335 | | 脑残+手残 | | 集合的关系
    凸轮大总结
    Floyd | | jzoj[1218] | | [Usaco2009 Dec]Toll 过路费 | | BZOJ 1774 | | 我也不知道该怎么写
    topsort | | jzoj[1226] | | NOIP2003神经网络
  • 原文地址:https://www.cnblogs.com/owenyu/p/6724574.html
Copyright © 2020-2023  润新知