jzoj4261. 【NOIP2015模拟10.22】最小代价

题目描述

Description
给出一幅由n个点m条边构成的无向带权图。
其中有些点是黑点,其他点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个黑点,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到离它最近的黑点的距离仍然等于原图中的最短距离。

Input
第一行两个整数n,m;
第二行n 个整数,0表示白点,1 表示黑点;
接下来m 行,每行三个整数x,y,z,表示一条连接x和y 点,权值为z 的边。
Output
如果无解,输出impossible;
否则,输出最小代价。

Sample Input
5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5
Sample Output
5
【样例解释】
选 2、4、6三条边

Data Constraint
对30%的输入数据: 1≤n≤10, 1≤m≤20;
对100%的输入数据:1≤n≤100000,1≤m≤200000,1≤z≤1000000000

伪100%

题解做法(没错题解是水法)
从超级源向黑点连边权为0的边,在最短路图上求最小生成树

然而——
这™随便出一组数据都能卡掉!!!
For example
5 5
1 0 0 0 0
1 2 2
2 3 2
3 4 2
1 5 5
5 4 1

Answer:10
然而很多人的程序都是7(包括我)
原因是因为没有选1–>5这条边,使得5连向的不是最短路

For example2
5 4
1 0 0 0 1
1 2 3
2 3 2
3 4 1
4 5 4

Answer:8
应该很多人输出的都是6(然而我一开始是10,原因是没有把所有黑点连在一起)
因为没有选4-5的边

code


#include 
#include 
#include 
#include 
#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

int a[500001][3];
struct AA{
    int x,y,z;
} A[500001];
int ls[100001];
int b[100001];
int d[1000001];
long long f[100001];
int fa[100001];
int n,m,i,j,k,l,len,h,t,M;
long long ans;

int gf(int t)
{
    if (fa[t]==t) return t;
    fa[t]=gf(fa[t]);

    return fa[t];
}

bool cmp(AA a,AA b)
{
    return a.zvoid New(int x,int y,int z)
{
    len++;
    a[len][0]=y;
    a[len][1]=z;
    a[len][2]=ls[x];
    ls[x]=len;
}

void qsrot(int l,int r)
{
    int i,j,mid=(l+r)/2;
}

int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)
    {
        scanf("%d",&b[i]);
        if (b[i]) New(0,i,0);

        fa[i]=i;
    }

    fo(i,1,m)
    {
        scanf("%d%d%d",&j,&k,&l);
        New(j,k,l);
        New(k,j,l);
    }

    memset(f,127,sizeof(f));
    h=0;t=1;
    d[1]=0;
    f[0]=0;
    while (hfor (i=ls[d[++h]]; i; i=a[i][2])
        if (f[a[i][0]]>f[d[h]]+a[i][1])
        {
            d[++t]=a[i][0];
            f[a[i][0]]=f[d[h]]+a[i][1];
        }
    }

    h=0;t=1;
    d[1]=0;
    while (hfor (i=ls[d[++h]]; i; i=a[i][2])
        if (f[a[i][0]]==f[d[h]]+a[i][1])
        {
            d[++t]=a[i][0];

            if (d[h])
            {
                M++;
                A[M].x=d[h];
                A[M].y=a[i][0];
                A[M].z=a[i][1];
            }
        }
    }
    sort(A+1,A+M+1,cmp);

    fo(i,1,M)
    if (gf(A[i].x)!=gf(A[i].y))
    {
        fa[fa[A[i].x]]=fa[A[i].y];
        ans+=A[i].z;
    }

    if (!ans)
    printf("impossible\n");
    else
    printf("%lld\n",ans);
}

伪100%*2

出现这种情况原因是没有选最短路上的边
于是每次先选唯一的最短路,之后再最小生成树
(然而还是会挂)

code

#include 
#include 
#include 
#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
#define max(a,b) (a>b?a:b)
using namespace std;

struct AA{
    int x,y;
}c[200001];

int a[400002][2];
int ls[200001];
int A[800002][2];
int Ls[400001];
int dfn[200001];
int low[200001];
bool bz[200001];
bool Bz[200002];
int d[200001];
int fa[400001][19];
int s[400001];
int D[400001];
int n,m,Q,i,j,k,l,len,Len,N,x,y;

void swap(int &x,int &y)
{
    int z=x;
    x=y;
    y=z;
}

bool cmp(AA a,AA b)
{
    return a.xvoid _New(int x,int y)
{
    len++;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;
}

void New(int x,int y)
{
    Len++;
    A[Len][0]=y;
    A[Len][1]=Ls[x];
    Ls[x]=Len;
}

void Dfs(int t)
{
    bz[t]=1;

    for (int i=ls[t]; i; i=a[i][1])
    if (!bz[a[i][0]])
    {
        Dfs(a[i][0]);
        if (t==1) k++;
    }
}

void dfs(int Fa,int t)
{
    int i,j;

    fa[t][0]=Fa;
    fo(j,1,18)
    fa[t][j]=fa[fa[t][j-1]][j-1];

    for (i=ls[t]; i; i=a[i][1])
    if (a[i][0]!=Fa)
    {
        D[a[i][0]]=D[t]+1;
        dfs(t,a[i][0]);
    }
}

void work(int Fa,int t)
{
    int i;

    for (i=ls[t]; i; i=a[i][1])
    if (a[i][0]!=Fa)
    {
        work(t,a[i][0]);
        s[t]+=s[a[i][0]];
    }
}

void tj(int t)
{
    int i,k,x,L,s=233333333;

    j++;
    dfn[t]=j;
    low[t]=j;
    bz[t]=0;

    k=0;

    for (i=ls[t]; i; i=a[i][1])
    if (!Bz[i>>1])
    {
        Bz[i>>1]=1;

        if (!dfn[a[i][0]])
        {
            d[++l]=i;
            tj(a[i][0]);

            if (low[a[i][0]]>=dfn[t] || t==1 && k>=2)
            {
                x=0;
                L=l;
                while (L>0 && a[d[L]][0]!=t)
                L--,x++;

                if (x>1)
                N++;

                while (l>0 && d[l+1]!=i)
                {
                    if (x>1)
                    {
                        New(a[d[l]][0],N);
                        New(N,a[d[l]][0]);
                    }

                    bz[a[d[l]][0]]=1;
                    l--;
                }
                if (x>1)
                {
                    New(t,N);
                    New(N,t);
                }
                else
                {
                    New(t,a[d[l+1]][0]);
                    New(a[d[l+1]][0],t);
                }
            }
        }

        if (!bz[a[i][0]])
        s=min(s,low[a[i][0]]);

        Bz[i>>1]=0;
    }

    low[t]=min(low[t],s);
}

int main()
{
    len=1;
    scanf("%d%d%d",&n,&m,&Q);N=n;
    fo(i,1,m)
    {
        scanf("%d%d",&c[i].x,&c[i].y);

        if (c[i].x>c[i].y)
        swap(c[i].x,c[i].y);
    }

    sort(c+1,c+m+1,cmp);
    fo(i,1,m)
    if (c[i].x!=c[i-1].x || c[i].y!=c[i-1].y)
    {
        _New(c[i].x,c[i].y);
        _New(c[i].y,c[i].x);
    }

    k=0;
    Dfs(1);

    j=0;l=0;
    tj(1);

    fo(i,1,Len)
    {
        a[i][0]=A[i][0];
        a[i][1]=A[i][1];
    }
    len=Len;
    fo(i,1,N)
    ls[i]=Ls[i];

    D[1]=1;
    dfs(0,1);
    for (;Q;Q--)
    {
        scanf("%d%d",&x,&y);

        s[x]++;s[y]++;

        if (D[x]18,0)
        if (D[fa[x][i]]>=D[y])
        x=fa[x][i];

        fd(i,18,0)
        if (fa[x][i]!=fa[y][i])
        x=fa[x][i],y=fa[y][i];

        if (x!=y)
        x=fa[x][0];

        s[x]--;
        s[fa[x][0]]--;
    }

    work(0,1);
    fo(i,1,n)
    printf("%d\n",s[i]);
}

真·100%

Iking123的方法

题目要求的方案要满足三个条件:
①最短路且走到黑点
②无重边
③权值最小

方法:
在求完最短路之后,对于每个白点选一条在最短路上且权值最小的边
没了

证明正确性,也就是证明上面的三个条件
①每次选最短路,结果肯定是最短路了,一直走最后一定走到黑点(黑点是最短路的起始点)
②如果x–>y是最短路的话,那么y–>x肯定不是最短路,所以x–>y–>z,不会有重边
③每次选权值最小的边

code

然而我跑得比lking123还要快

#include 
#include 
#include 
#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
using namespace std;

int a[500001][3];
int ls[100001];
int b[100001];
int d[1000001];
long long f[100001];
int n,m,i,j,k,l,len,h,t,M;
long long ans;

void New(int x,int y,int z)
{
    len++;
    a[len][0]=y;
    a[len][1]=z;
    a[len][2]=ls[x];
    ls[x]=len;
}

int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,n)
    {
        scanf("%d",&b[i]);
        if (b[i]) New(0,i,0);
    }

    fo(i,1,m)
    {
        scanf("%d%d%d",&j,&k,&l);
        New(j,k,l);
        New(k,j,l);
    }

    memset(f,127,sizeof(f));
    h=0;t=1;
    d[1]=0;
    f[0]=0;
    while (hfor (i=ls[d[++h]]; i; i=a[i][2])
        if (f[a[i][0]]>f[d[h]]+a[i][1])
        {
            d[++t]=a[i][0];
            f[a[i][0]]=f[d[h]]+a[i][1];
        }
    }

    fo(i,1,n)
    if (!b[i])
    {
        k=1233333333;
        for (j=ls[i]; j; j=a[j][2])
        if (f[a[j][0]]+a[j][1]==f[i])
        k=min(k,a[j][1]);

        if (k==1233333333)
        {
            printf("impossible\n");
            return 0;
        }
        ans+=k;
    }

    printf("%lld\n",ans);
}

你可能感兴趣的:(OJ题解)