【GDOI2016Day2】第一题SigemaGO

来源:互联网 发布:朝鲜 知乎 编辑:程序博客网 时间:2024/05/22 00:34

题目大意

给出一个n个点m条边有向图。你可以添加最多lim条长度为L边,添加的边(x,y)必须满足存在一个z,使得原图中存在有向边(x,z)、(z,x)。求添加边后点1到n的最短路。

Data Constraint

n≤10000 m≤50000 lim≤5 所有边权和L均为正整数且≤1000

分析

n和lim都较小,所以可以尝试从这里入手。
如果把图分成lim+1层,那么可以用图的第i层(0≤i≤lim)表示添加了lim条边。
然后该如何从上一层跳到下一层呢?
枚举上述的z,然后枚举原图中连向z的边和z的出边。直接连接的复杂度较大,所以要对这个z开个新点z’,然后x向z’、z’向y连接一条边。这样复杂度只有O(m)
对每一层向下一层都这样连边,同一层的边则与原图相同。

然后从第0层的点1出发,跑一遍最短路即可(这里我打的时Dij+heap,数据没有卡spfa)。
时间复杂度为O(mlimLog(n))

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn=110005,maxm=800005;int n,m,N,lim,tot,tt,L,h[maxn],e[maxm],next[maxm],dis[maxm],f[maxm],H[maxn],E[maxm],Next[maxm],id[maxn],pos[maxn];void add(int x,int y,int D){    e[++tot]=y; dis[tot]=D; next[tot]=h[x]; h[x]=tot;}void Add(int x,int y){    E[++tt]=y; Next[tt]=H[x]; H[x]=tt;}void up(int x){    if (x==1)    {        pos[id[1]]=1;        return;    }    int fa=x/2;    if (f[id[fa]]>f[id[x]])    {        id[fa]^=id[x]^=id[fa]^=id[x];        up(fa);    }    pos[id[x]]=x;}void down(int x){    if (x*2>n)    {        pos[id[x]]=x;        return;    }    int son=(x*2==n || f[id[x*2]]<=f[id[x*2+1]])?x*2:x*2+1;    if (f[id[x]]>f[id[son]])    {        id[x]^=id[son]^=id[x]^=id[son];        down(son);    }    pos[id[x]]=x;}int main(){    freopen("sigemago.in","r",stdin); freopen("sigemago.out","w",stdout);    scanf("%d%d%d%d",&N,&m,&L,&lim);    while (m--)    {        int x,y,D;        scanf("%d%d%d",&x,&y,&D);        Add(y,x);        for (int i=0;i<=lim;i++) add(x+i*N,y+i*N,D);    }    n=(lim+1)*N;    for (int i=1;i<=N;i++)    {        for (int k=0;k<lim;k++)        {            n++;            for (int j=H[i];j;j=Next[j]) add(E[j]+k*N,n,L);            for (int j=h[i];j;j=next[j]) if (e[j]<=(lim+1)*N)add(n,e[j]+(k+1)*N,0);  //这个if绝对不能漏        }    }    memset(f,42,sizeof(f));    f[1]=0;    for (int i=1;i<=n;i++) pos[i]=id[i]=i;    while (n--)    {        int x=id[1]; id[1]=id[n+1]; pos[id[1]]=1;        down(1);        for (int i=h[x];i;i=next[i]) if (f[x]+dis[i]<f[e[i]])        {            f[e[i]]=f[x]+dis[i];            up(pos[e[i]]);        }    }    int ans=1e8;    for (int i=0;i<=lim;i++) ans=min(ans,f[N+i*N]);    if (ans==1e8) printf("-1\n");else printf("%d\n",ans);    fclose(stdin); fclose(stdout);    return 0;}
0 0