【结论】【非严格次小生成树】NKOJ3855 merlin

NKOJ3855 merlin
时间限制 : - MS 空间限制 : 165536 KB
评测说明 : 2s

问题描述
古月族是一个神奇的种族,有一天古月腾和古月豪在一起看《梅林传奇(merlin)》。
剧中,以卡梅洛特(camelot)为代表的是古时英格兰的城池坐落在现我大英帝国的某处土地上,每个城池被乌瑟王(亚瑟王之父)编号,方便货物来往。
假设有n座城池,编号为1-n,一些城池之间有单向道连接(逆向行驶会被视为私自入境),共k条。如果有一些城池能够互相到达交通无碍,他们会结为盟友,于是大英帝国上出现了许多帮派,他们要么互相挑起战争,要么选择井水不犯河水,形势极其尴尬,甚至,他们把连到其他帮派外的单向边拆毁了。
现在,乌瑟王死后,亚瑟王想要统一大英,他决定让魔法师梅林在一些城池之间修一些双向边,这样能使所有城池互相联系,交流,促进和平发展。梅林为了帮助亚瑟王,他把可能会修的道路都进行了魔法加特,使走上这条路的人心平气和,向往和平。虽然梅林魔法强大,但他也需要休息,他只能在指定的城池之间建立m条双向边,并且每条双向边上的有一个魔力需求值wi。
梅林已经累趴下了,谁叫亚瑟王这么折磨他(可怜的梅子),他想知道;
怎样连边,才能让所有城池能够互相到达,并且他耗费的魔力最少,求出这个最小值。
是否有不同种建边的方法。

输入格式
多组测试数据。
第一行一个正整数T,对于每一组测试数据:
第一行,三个个整数n,k,m。
接下来k行,每行两个整数,x,y,表示从x城池到y城池有一条单向道路。
接下来m行,每行三个整数,x,y,z,表示可以在城池x和城池y之间,建立一条双向道路,消耗魔法值为z。

输出格式
对于每一组测试数据:
第一行,一个整数表示消耗最少魔力。
第二行,若有不同的建边方法,输出“YES”,否则输出“NO”(均不含引号)。

样例输入 1
1
5 5 5
1 2
2 3
3 1
3 4
4 5
1 2 10
4 5 9
5 1 3
3 4 6
2 4 1

样例输出 1
4
NO

样例输入 2
1
5 5 5
1 2
2 3
3 4
4 5
5 1
1 2 100
2 3 123
4 3 1
1 2 1
2 3 1

样例输出 2
0
NO

样例输入 3
1
5 6 3
1 2
2 1
1 3
3 4
4 5
5 3
1 3 5
2 4 5
3 5 999

样例输出 3
5
YES

提示
【数据范围】
对于30%的数据, 1<=n<=1000,1<=T<=5。
对于100%的数据,1<=n<=500000,1<=m<=100000,1<=k<=1000000 ,1<=x<=n,1<=y<=n,0<=z<=10000,1<=T<=5。
保证m条双向边能够使所有城池交流畅通无阻。
注:有兴趣的同学可以思考,如果每个帮派不把连接到外帮派的单向边拆毁,应该怎么做。(本次考试请根据题目描述做题)。

来源 ht hjh

第一问:强连通分量缩点-> 最小生成树
第二问:

法一:依次加边、减掉环中除加入边外最大边(lca倍增讨论)->生成树大小增加否

#include
#include
#include
#include
using namespace std;
const int needn=500003;
const int needm=100003;
const int needk=1000003;
const double lg2=log(2);

int n,k,m,mm;
//...........................................................
inline void in_(int & d)
{
    char t=getchar();
    while(t<'0'||t>'9') t=getchar();
    for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';
}
//...........................................................
int tot,fi[needn],la[needk],en[needk],len[needk];
bool vis[needn];

void add(int a,int b,int c=0)
{
    tot++;
    la[tot]=fi[a];
    fi[a]=tot;
    en[tot]=b;
    len[tot]=c;
}
//...........................................................
int visittime,dfn[needn],low[needn],scc,id[needn];
int top,ss[needn];
bool ins[needn];

void tarjan(int x)
{
    low[x]=dfn[x]=++visittime;
    ss[++top]=x,ins[x]=true;
    for(int t=fi[x],y;t;t=la[t])
    {
        y=en[t];
        if(!dfn[y]) 
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        int y;
        scc++;
        do{
            y=ss[top],top--;
            ins[y]=false;
            id[y]=scc;
        }while(y!=x);
    }
}
//...........................................................
struct fy
{
    int a,b,len;
    bool use;
    bool operator< (const fy& b) const
    {
        return len//...........................................................
int be[needn];

int getbe(int x)
{
    return x==be[x] ?x :be[x]=getbe(be[x]);
}

void clean()
{
    for(int i=1;i<=scc;i++) be[i]=i;
}
//...........................................................
int ans1;
int dm;
int dep[needn],mdis[needn][22],fa[needn][22];

void build_lca(int x)
{
    vis[x]=true;
    int k=log(dep[x])/lg2;
    dm=max(dm,k);
    for(int i=1;i<=k;i++) 
     fa[x][i]=fa[fa[x][i-1]][i-1],mdis[x][i]=max(mdis[x][i-1],mdis[fa[x][i-1]][i-1]);
    for(int t=fi[x],y;t;t=la[t])
    {
        y=en[t];
        if(vis[y]) continue;
        dep[y]=dep[x]+1;
        fa[y][0]=x;
        mdis[y][0]=len[t];
        build_lca(y);
    }
}

bool lca(int k)
{
    int a=w[k].a,b=w[k].b;
    if(dep[a]>dep[b]) swap(a,b);
    int ans=0,c;
    for(int i=0,p=dep[b]-dep[a];i<=dm;i++) if((p>>i)&1) ans=max(ans,mdis[b][i]),b=fa[b][i];
    if(a==b) c=a;
    else 
    {
         for(int i=dm;i>=0;i--) 
         if(fa[a][i]!=fa[b][i])
         {
            ans=max(ans,mdis[a][i]),ans=max(ans,mdis[b][i]);
            b=fa[b][i],a=fa[a][i];
         }
         c=fa[a][0];
         ans=max(ans,mdis[a][0]),ans=max(ans,mdis[b][0]);
    }
    if(ans!=w[k].len) return false;
    return true;
}
//...........................................................
void ini()
{
    ans1=tot=0;
    scc=visittime=top=0;
    for(int i=1;i<=n;i++) vis[i]=fi[i]=dfn[i]=low[i]=id[i]=ins[n]=0;
    for(int i=1;i<=m;i++) w[i].use=false;
    for(int i=0,j;i<=dm;i++) for(j=1;j<=n;j++) mdis[j][i]=fa[j][i]=0;
}

//...........................................................
int main()
{
    int t;scanf("%d",&t);
while(t--)
{
    scanf("%d%d%d",&n,&k,&m);
    ini();
    for(int i=1,a,b;i<=k;i++)
    {
        in_(a),in_(b);
        add(a,b);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    mm=0;
    for(int i=1,a,b,c;i<=m;i++)
    {
        in_(a),in_(b),in_(c);
        if(id[a]==id[b]) continue;
        mm++;
        w[mm].a=id[a],w[mm].b=id[b],w[mm].len=c;
    }
    sort(w+1,w+1+mm);
    clean();
    for(int i=1;i<=scc;i++) fi[i]=0;
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
        {
            be[x]=y;
            ans1+=w[i].len;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }
    build_lca(1);
    bool getans2=false;
    for(int i=1;i<=mm;i++)
    {
        if(!w[i].use&&lca(i)) 
        {
            getans2=true;
            break;
        }
    }
    printf("%d\n",ans1);
    puts(getans2 ? "YES":"NO");
}
}

法二:找出最小生成树后将所有边重新排序,使新的顺序为原来顺序中长度相等的元素反序,重新找最小生成树,如果新加入的边为原来未讨论过的,则YES。

struct fy
{
    int a,b,len,id;
    bool use;
};
bool way1 (const fy& x,const fy& y) 
{
    if(x.len==y.len) return x.idreturn x.lenlen;
}
bool way2 (const fy& x,const fy& y) 
{
    if(x.len==y.len) return x.id>y.id;
    return x.lenlen;
}
........

    int ans1=0;
    sort(w+1,w+1+mm,way1);
    clean();
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
        {
            be[x]=y;
            ans1+=w[i].len;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }

    bool getans2=false;
    sort(w+1,w+1+mm,way2);
    clean();
    for(int i=1,x,y,cnt=0;i<=mm&&cnt+1if(x!=y)
        {
            if(!w[i].use) 
            {
                getans2=true;
                break;
            }
            be[x]=y;
            w[i].use=true;
            cnt++;
            add(w[i].a,w[i].b,w[i].len),add(w[i].b,w[i].a,w[i].len);
        }
    }
    printf("%d\n",ans1);
    puts(getans2 ? "YES":"NO");

你可能感兴趣的:(【结论】【非严格次小生成树】NKOJ3855 merlin)