cdqz2017-test11-奇诺之旅(拟阵)

解为基环树森林

证明其具有拟阵的性质:

1、空集独立

2、基环树森林的子集仍然是基环树森林,满足遗传特性

3、对于基环树森林A,B,若|A|<|B| (边数),一定可以找到一条边e∈B,∉A,使A∪e仍然是基环树森林,满足扩充特性

扩充特性证明:

枚举A中的联通块

1、若A中这个联通块的边数<B中对应联通块的边数,显然存在一条满足要求的边

2、否则B中存在一条边连接了A的两个联通块。枚举B中这样的边,若这两个联通块至少有一个不是基环树,则该边满足要求。

否则,A中这两个联通块都是基环树,B中不是基环树,因此B中这个联通块的边数<A中这两个联通块的边数和。所以还存在其他的联通块之间的边满足要求

所以本题解法:

按边权从大到小排序,先构造出一颗最大权值基环树

然后按边权从大到小枚举之前没有用过的边u--v

首先明确不会存在原来联通块是树,加上这条边变成基环树的情况

因为之前已经构造了最大权值基环树

1、若u和v在同一个联通块,该联通块一定是基环树,那么新加的这条边可以替换环上的任意一条边,也可以替换u和v到环的路径上的一条边

2、若u和v不在同一个联通块,那么这两个联通块都是基环树,这条边可以替换两个环上的任意一条边,也可以替换u和v到环的路径上的一条边

无论哪种情况,都是用当前边去替换最大权值基环树上的边

遇上已经被替换过一次的边则停止替换,因为之前用的边的边权不会比现在的边的边权小

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

#define N 200001

struct node
{
    int u,v,w;
    int id;
    bool use;
}e[N];

LL ans[N];

int fa[N],siz_p[N],siz_e[N];
int circle[N];//基环的环在哪儿 

struct graph
{
    int from,to,id,w;
    int nxt;
}g[N<<1];
int tot,front[N];
int pre[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}

bool cmp(node p,node q)
{
    return p.w>q.w;
}

int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); }

void unionn(int u,int v)
{
    int fu=find(u),fv=find(v);
    if(fu==fv)
    {
        circle[fu]=u;
        siz_e[fu]++;
        return;
    }
    fa[fu]=fv;
    siz_e[fv]+=siz_e[fu]+1;
    siz_p[fv]+=siz_p[fu];
    circle[fv]|=circle[fu];
}

void add(int u,int v,int t)
{
    g[++tot].from=u; g[tot].to=v; g[tot].id=e[t].id; g[tot].w=e[t].w; g[tot].nxt=front[u]; front[u]=tot;
    g[++tot].from=v; g[tot].to=u; g[tot].id=e[t].id; g[tot].w=e[t].w; g[tot].nxt=front[v]; front[v]=tot;
}

int dfs(int now,int last)
{
    int tmp=0,t;
    for(int i=front[now];i;i=g[i].nxt)
    {
        t=g[i].to;
        if(t==last) continue;
        if(pre[t]==-1) tmp=i;
        if(pre[t]) continue;
        pre[t]=i;
        tmp|=dfs(t,now);
    }
    return tmp;
}

void concat(int x,int w)
{
    int now;
    while(1)
    {
        now=pre[x];
        if(ans[g[now].id]) break;
        ans[g[now].id]=ans[0]-g[now].w+w;
        x=g[now].from;
    }
}

void out(LL x)
{
    if(x>9) out(x/10);
    putchar(x%10+'0');
} 

int main()
{
    freopen("journey.in","r",stdin);
    freopen("journey.out","w",stdout);
    int n,m;
    read(n); read(m);
    for(int i=1;i<=m;++i) 
    {
        read(e[i].u); read(e[i].v); read(e[i].w);
        e[i].id=i;
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;++i)
    {
        fa[i]=i;
        siz_p[i]=1;
    }
    int fu,fv;
    for(int i=1;i<=m;++i)
    {
        fu=find(e[i].u);
        fv=find(e[i].v);
        if(siz_p[fu]==siz_e[fu] && siz_p[fv]==siz_e[fv]) continue;
        ans[0]+=e[i].w;
        e[i].use=true;
        unionn(e[i].u,e[i].v);
        add(e[i].u,e[i].v,i);
        
    }
    int u;
    for(int i=1;i<=n;++i)
    {
        u=find(i);
        if(circle[u]) u=circle[u];
        if(!pre[u])
        {
            pre[u]=-1;
            pre[u]=dfs(u,-1);
        }
    }
    for(int i=1;i<=m;++i)
    {
        if(e[i].use) continue;
        ans[e[i].id]=ans[0];
        concat(e[i].u,e[i].w);
        concat(e[i].v,e[i].w);
    }
    for(int i=1;i<=m;++i)
        if(!ans[e[i].id]) ans[e[i].id]=ans[0]-e[i].w;
    for(int i=1;i<=m;++i) out(ans[i]),putchar('
');
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8710568.html