【BZOJ】4912: [Sdoi2017]天才黑客-最短路&连边优化

传送门:bzoj4912


题解

最短路。

发现从点到点时的代价是不确定的,而从边到边的代价是一定的,所以将边转化为点,点权 v a l i val_i vali为全图中的边权,设所有以1为起点的边 d i s i = v a l i dis_i=val_i disi=vali,建图连边跑最短路后,对于点 x x x,求出所有指向 x x x的边的 m i n ( d i s i ) min(dis_i) min(disi)就得到了根到 x x x的最短路。

新图中点 x x x到点 y y y的花费即为 v a l y + val_y+ valy+trie树中 L C A ( d x , d y ) LCA(d_x,d_y) LCA(dx,dy)的深度。

代表某条边的点会和所有其他与这条边端点有交集的边之间连边,最坏情况连边多达 n 2 n^2 n2条(菊花图)。

考虑优化连边:
t r i e trie trie树上的点按dfs序排序后能够发现 l c p ( d x , d y ) lcp(d_x,d_y) lcp(dx,dy)具有与后缀数组类似的性质:设 l e n x = l c p ( d x , d x + 1 ) len_x=lcp(d_x,d_{x+1}) lenx=lcp(dx,dx+1),则 l c p ( d x , d y ) = m i n ( l e n x , l e n x + 1 , . . , l e n y − 1 ) lcp(d_x,d_y)=min(len_{x},len_{x+1},..,len_{y-1}) lcp(dx,dy)=min(lenx,lenx+1,..,leny1)。而 l c p lcp lcp的取值也只有 k k k种。

于是对于原图中每个点相关的边,将 d i d_i di按dfs排序,枚举分界点 x x x,所有标号 ≤ x \leq x x到标号 ≥ x \geq x x的点之间的距离必然不超过 l e n x len_x lenx,所以分别拆成前缀入度出度和后缀入度出度。

点数是 O ( m ) O(m) O(m)的复杂度 O ( m l o g m ) O(mlogm) O(mlogm)


代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+100,M=2e6+100,inf=0x7f7f7f7f;
 
int n,m,K,tk,q[N],df[N],len,val[N*10],dis[N*10];
int head[N*10],to[M],nxt[M],w[M],tot;
int xi[N],xt[N],yi[N],yt[N],cnt;
 
char buf[N];
inline char gc()
{
    static int p1=0,p2=0;
    if(p1==p2) p1=0,p2=fread(buf,1,N,stdin);
    if(p1==p2) return EOF;
    return buf[p1++];
}
 
char cp,OS[100];
inline void rd(int &x)
{
    cp=gc();x=0;
    for(;!isdigit(cp);cp=gc());
    for(;isdigit(cp);cp=gc()) x=(x<<3)+(x<<1)+(cp^48);
}
 
char wbuf[N];int p3;
inline void wchar(char x)
{
    if(p3==N) fwrite(wbuf,1,N,stdout),p3=0;
    wbuf[p3++]=x;
}
 
inline void ot(int x)
{
    int re=0;
    for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
    OS[0]='\n';
    for(;~re;--re) wchar(OS[re]);
}
 
struct E{
   int a,b,c,d;
   inline void ini(){rd(a);rd(b);rd(c);rd(d);}
}e[N];
 
struct Dr{
    int head[N],to[N],nxt[N],tot;
    inline void itia(){memset(head,0,sizeof(int)*(n+1));tot=0;}
    inline void lk(int u,int v)
    {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
}di,dt;
 
struct Trie{
   int head[N],to[N],nxt[N],tot;
   int son[N],top[N],f[N],dep[N],sz[N],dfn;
    
   inline void itia(){memset(head,0,sizeof(int)*(K+1));tot=dfn=0;}
    
   inline void lk(int u,int v)
   {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
    
   void dfs(int x)
   {
       sz[x]=1;son[x]=0;
       for(int j,i=head[x];i;i=nxt[i]){
          j=to[i];dep[j]=dep[x]+1;f[j]=x;dfs(j);
          sz[x]+=sz[j];if(sz[son[x]]<sz[j]) son[x]=j;
       }
   }
    
   void dfss(int x,int tpo)
   {
       top[x]=tpo;df[x]=++dfn;
       if(!son[x]) return;
       dfss(son[x],tpo);
       for(int j,i=head[x];i;i=nxt[i]){
          j=to[i];if(j==son[x]) continue;
          dfss(j,j);
       }
   }
    
   inline void build()
   {
       int i,x,y,z;
       for(i=1;i<K;++i) {rd(x);rd(y);rd(z);lk(x,y);}
       dfs(1);dfss(1,1);
   }
    
   inline int LCA(int x,int y)
   {
       for(;top[x]!=top[y];x=f[top[x]]) 
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
       return min(dep[x],dep[y]);
   }
}trie;
 
inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}
 
inline bool cmp(const int&x,const int&y){return df[e[abs(x)].d]<df[e[abs(y)].d];}
 
inline void mk(int x)
{
    if((!di.head[x])||(!dt.head[x])) return;
    int i,j;len=0;
    for(i=di.head[x];i;i=di.nxt[i]) q[++len]=di.to[i];
    for(i=dt.head[x];i;i=dt.nxt[i]) q[++len]=-dt.to[i];
    sort(q+1,q+len+1,cmp);
    for(i=1;i<=len;++i){
        xi[i]=++cnt;xt[i]=++cnt;yi[i]=++cnt;yt[i]=++cnt;
        if(i>1){
            lk(xi[i-1],xi[i],0);lk(xt[i-1],xt[i],0);
            lk(yi[i],yi[i-1],0);lk(yt[i],yt[i-1],0);
        }
        if(q[i]>0) {lk(q[i],xi[i],0);lk(q[i],yi[i],0);}
        else {q[i]=-q[i];lk(xt[i],q[i],0);lk(yt[i],q[i],0);}
    }
    for(i=1;i<len;++i){
        j=trie.LCA(e[q[i]].d,e[q[i+1]].d);
        lk(xi[i],xt[i+1],j);lk(yi[i+1],yt[i],j);
    }
     
}
 
struct Pr{
   int id,v;
   bool operator<(const Pr&ky)const{
       return ky.v<v;
   }
}tp;
 
priority_queue<Pr>que;
inline void spfa()
{
    memset(dis,0x7f,sizeof(dis));
    int i,j,x;
    for(i=1;i<=m;++i) if(e[i].a==1){dis[i]=val[i];que.push((Pr){i,dis[i]});}
    for(;que.size();){
        tp=que.top();que.pop();
        x=tp.id;if(dis[x]!=tp.v) continue;
        for(i=head[x];i;i=nxt[i]){
            j=to[i];if(dis[j]<=dis[x]+val[j]+w[i]) continue;
            dis[j]=dis[x]+val[j]+w[i];que.push((Pr){j,dis[j]});
        }
    }
}
 
inline void sol()
{
    int i,j,x,y,z;
    memset(head,0,sizeof(int)*(cnt+1));memset(val,0,sizeof(int)*(m+1));
    tot=0;trie.itia();di.itia();dt.itia();
    rd(n);rd(m);rd(K);cnt=m;
    for(i=1;i<=m;++i){
        e[i].ini();val[i]=e[i].c;
        dt.lk(e[i].a,i);di.lk(e[i].b,i);
    }
    trie.build();
    for(i=1;i<=n;++i) mk(i);
    spfa();
    for(i=2;i<=n;++i){
        for(z=inf,j=di.head[i];j;j=di.nxt[j]) 
          z=min(z,dis[di.to[j]]);
        ot(z);
    }
}
 
int main(){
    for(rd(tk);tk;--tk) sol();
    if(p3) fwrite(wbuf,1,p3,stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值