[最短路 虚树+线段树优化建图] BZOJ 4912 [Sdoi2017]天才黑客

本文介绍了一种针对SPFA算法的优化方法,通过记录边的状态而非节点状态来提高效率,并结合虚树构建和线段树优化实现复杂度的有效控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sro clrs97老师:”T1特别好写” orz
听说SPFA被卡了 /点蜡

这个东西跑最短路,状态肯定不能指记录点,还要记录是哪条边走过来的,那干脆就直接记录边
把边变成点,把点变成连在边之间的边

我是把一条边拆成两个点,中间设为边的费用
lcp的费用在点变成的边上,这个东西裸的连,在原图一个点上是O(deg2)的肯定炸
那么就建一颗虚树,然后枚举lca,把lca是这个的点两两之间都连边为这个lca的深度,这个可以枚举lca的一个儿子,这个儿子子树中所有点向其他儿子子树中所有点连边,这个可以dfs序加线段树优化下

然后就好了... 我写了 6KB

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define pb push_back 
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<ll,int> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void print(ll x){
  if (x>=10) print(x/10);
  putchar('0'+x%10);
}

const int N=100005;

vector<int> ind[N],outd[N];

#define V G[p].v

namespace S{ //SAAP
  const int N=2000005;
  const int M=4000005;
  struct edge{
    int u,v,w,next;
  }G[M<<1];
  int head[N],inum;
  inline void add(int u,int v,int w){
    int p=++inum; G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;
  }
  inline void clear(){
    cl(head); inum=0;
  }
  ll dis[N];
  priority_queue<abcd> Q;
  inline void Dij(int ncnt){
    while (!Q.empty()) Q.pop();
    for (int i=1;i<=ncnt;i++) dis[i]=1LL<<60;
    for (int i=0;i<(int)outd[1].size();i++)
      dis[outd[1][i]]=0,Q.push(abcd(0,outd[1][i]));
    while (!Q.empty()){
      int u=Q.top().second; ll d=Q.top().first; Q.pop();
      if (dis[u]!=-d) continue;
      for (int p=head[u];p;p=G[p].next)
    if (dis[V]>dis[u]+G[p].w){
      dis[V]=dis[u]+G[p].w;
      Q.push(abcd(-dis[V],V));
    }
    }
  }
}

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;

inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

const int KK=18;

int fat[N][KK],depth[N];
int pre[N],clk;

inline void dfs(int u,int fa){
  fat[u][0]=fa; depth[u]=depth[fa]+1; pre[u]=++clk;
  for (int k=1;k<KK;k++) fat[u][k]=fat[fat[u][k-1]][k-1];
  for (int p=head[u];p;p=G[p].next)
    if (V!=fa)
      dfs(V,u);
}
inline int LCA(int u,int v){
  if (depth[u]<depth[v]) swap(u,v);
  for (int k=KK-1;~k;k--)
    if ((depth[u]-depth[v])>>k&1)
      u=fat[u][k];
  if (u==v) return u;
  for (int k=KK-1;~k;k--)
    if (fat[u][k]!=fat[v][k])
      u=fat[u][k],v=fat[v][k];
  return fat[u][0];
}

int n,m,K;
int iu[N],iv[N],w[N],pos[N];

int ncnt;

namespace XS{
  const int N=2000005;
  struct edge{
    int u,v,next;
  }G[N];
  int head[N],inum;
  int vst[N],Clk;
  inline int &Head(int u){
    return vst[u]!=Clk?vst[u]=Clk,head[u]=0:head[u];
  }
  inline void add(int u,int v){
    int p=++inum; G[p].u=u; G[p].v=v; G[p].next=Head(u); Head(u)=p;
  }
  int pre[N],clk,back[N];
  int size[N];
  inline void dfs(int u){
    pre[u]=++clk; back[clk]=u; size[u]=1;
    for (int p=Head(u);p;p=G[p].next)
      dfs(V),size[u]+=size[V];
  }
  int lrt,rrt,ls[N],rs[N];
  int lpos[N],rpos[N];
  inline void Build(int &x,int l,int r,bool lft){
    x=++ncnt;
    if (l==r){
      if (lft) lpos[l]=x; else rpos[l]=x;
      return;
    }
    int mid=(l+r)>>1;
    Build(ls[x],l,mid,lft); Build(rs[x],mid+1,r,lft);
    if (lft) S::add(ls[x],x,0); else S::add(x,ls[x],0);
    if (lft) S::add(rs[x],x,0); else S::add(x,rs[x],0);
  }
  inline void Link(int x,int l,int r,int ql,int qr,int idx,bool lft){
    if (ql<=l && r<=qr){
      if (lft) S::add(x,idx,0); else S::add(idx,x,0);
      return;
    }
    int mid=(l+r)>>1;
    if (ql<=mid) Link(ls[x],l,mid,ql,qr,idx,lft);
    if (qr>mid) Link(rs[x],mid+1,r,ql,qr,idx,lft);
  }
  inline void LLink(int a,int b,int c,int d,int x){
    if (a>b || c>d) return;
    Link(lrt,1,clk,a,b,ncnt+1,1);
    Link(rrt,1,clk,c,d,ncnt+2,0);
    S::add(ncnt+1,ncnt+2,x);
    ncnt+=2;
  }
  inline void Work(int Rt,int x){
    clk=0; dfs(Rt);
    Build(lrt,1,clk,1); Build(rrt,1,clk,0);

    for (int j=0;j<ind[x].size();j++) S::add(ind[x][j]+m,lpos[pre[pos[ind[x][j]]]],0);
    for (int j=0;j<outd[x].size();j++) S::add(rpos[pre[pos[outd[x][j]]]],outd[x][j],0);

    for (int i=1;i<=clk;i++){
      int u=back[i];
      LLink(pre[u],pre[u],pre[u],pre[u]+size[u]-1,depth[u]-1);
      for (int p=Head(u);p;p=G[p].next){
    LLink(pre[V],pre[V]+size[V]-1,pre[u],pre[V]-1,depth[u]-1);
    LLink(pre[V],pre[V]+size[V]-1,pre[V]+size[V],pre[u]+size[u]-1,depth[u]-1);
      }
    }
  }
}

int tot,a[N<<2];
int sta[N<<2],pnt;
inline bool cmp(int x,int y){
  return pre[x]<pre[y];
}

inline void Solve(int x){
  tot=0;
  for (int j=0;j<ind[x].size();j++) a[++tot]=pos[ind[x][j]];
  for (int j=0;j<outd[x].size();j++) a[++tot]=pos[outd[x][j]];
  sort(a+1,a+tot+1,cmp); tot=unique(a+1,a+tot+1)-a-1;

  pnt=0; int rt=0;
  XS::Clk++; XS::inum=0;
  for (int i=1;i<=tot;i++){
    if (!rt || depth[a[i]]<depth[rt]) rt=a[i];
    if (!pnt) {
      sta[++pnt]=a[i]; continue;
    }
    int lca=LCA(sta[pnt],a[i]);
    if (!rt || depth[lca]<depth[rt]) rt=lca;
    while (pnt && depth[lca]<depth[sta[pnt]]){
      if (depth[lca]>=depth[sta[pnt-1]]){
    XS::add(lca,sta[pnt]);
    if (sta[--pnt]!=lca) sta[++pnt]=lca;
    break;
      }
      pnt--,XS::add(sta[pnt],sta[pnt+1]);
    }
    sta[++pnt]=a[i]; 
  }
  while (--pnt) XS::add(sta[pnt],sta[pnt+1]);

  XS::Work(rt,x);
}

ll Dis[N];

int main(){
  int T; int x,y,z;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(T);
  while (T--){
    read(n); read(m); read(K);
    for (int i=1;i<=m;i++){
      read(iu[i]); read(iv[i]); read(w[i]); read(pos[i]);
      ind[iv[i]].pb(i); outd[iu[i]].pb(i);
    }
    for (int i=1;i<K;i++)
      read(x),read(y),read(z),add(x,y,++inum);
    dfs(1,0);
    for (int i=1;i<=m;i++)
      S::add(i,i+m,w[i]);
    ncnt=2*m;
    for (int i=1;i<=n;i++)
      if (ind[i].size() && outd[i].size())
    Solve(i);
    S::Dij(ncnt);
    for (int i=1;i<=n;i++) Dis[i]=1LL<<60;
    for (int i=1;i<=m;i++)
      Dis[iv[i]]=min(Dis[iv[i]],S::dis[m+i]);
    for (int i=2;i<=n;i++)
      print(Dis[i]),putchar('\n');
    cl(head); inum=0; clk=0;
    ncnt=0; S::clear(); for (int i=1;i<=n;i++) ind[i].clear(),outd[i].clear();
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值