bzoj3694: 最短路(樹鏈剖分/並查集)

  bzoj1576的幫咱們跑好最短路版本23333(雙倍經驗!嘿嘿嘿ios

  這題能夠用樹鏈剖分或並查集寫。樹鏈剖分很是顯然,並查集的寫法比較妙,漲了個姿式,原來並查集的路徑壓縮還能這麼用...ide

  首先對於不在最短路徑樹上的邊x->y,設t爲最短路徑樹上lca(x,y),則t到y上的路徑上的點i到根的距離均可以用h[x]+dis[x][y]+h[y]-h[i](h[]爲深度)來更新,由於h[i]必定,只要讓h[x]+dis[x][y]+h[y]最小就行,這裏用樹剖直接修改整條鏈上的數,就能夠過了。ui

  並查集的方法就很巧妙了...把不在最短路徑樹上的邊找出來,按照h[x]+dis[x][y]+h[y]從小到大排序。而後按排序後的邊的順序更新答案,被更新過了的必然不會被再次更新。更新的方法就是每次兩個指針從x和y一步步向t靠近並更新沿途上沒更新過的點,同時用並查集記錄這些更改過的點的頂部,下次更新下面跑到這裏的點直接就能夠跳到沒修改的地方。好像感受其實就是把樹剖改爲用並查集來跳跳跳而已...spa

  只寫了並查集,樹剖的下次補(QAQ模板都不會打了已經指針

  UPD 2017.6.28:今天覆習了下樹剖,而後把這題寫了嘿嘿嘿。這題最後只查詢葉子結點,因此就不用上傳了,很是好寫。code

樹鏈剖分:blog

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ll long long 
using namespace std;
const int maxn=500010,inf=1000000000;
struct poi{int too,pre,sum;}e[maxn];
struct tjm{int sum,tag;}a[maxn];
struct zs{int x,y,len;}edge[maxn];
int n,m,x,y,z,flag,tot,tot2,cnt;
int last[maxn],size[maxn],fa[maxn],dep[maxn],son[maxn],w[maxn],top[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
void add(int x,int y,int z){e[++tot].too=y;e[tot].sum=z;e[tot].pre=last[x];last[x]=tot;}
void dfs1(int x)
{
    size[x]=1;
    for(int i=last[x];i;i=e[i].pre)
    {
        int too=e[i].too;
        if(too!=fa[x])
        {
            fa[too]=x;
            dep[too]=dep[x]+e[i].sum;
            dfs1(too);
            if(size[too]>size[son[x]])son[x]=too;
            size[x]+=size[too];
        }
    }
}
void dfs2(int x,int tp)
{
    w[x]=++cnt;top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=last[x];i;i=e[i].pre)
    if(e[i].too!=son[x]&&e[i].too!=fa[x])
    dfs2(e[i].too,e[i].too);
}
void pushdown(int x)
{
    if(a[x].tag==inf)return;
    int tag=a[x].tag;a[x].tag=inf;
    a[x<<1].tag=min(tag,a[x<<1].tag);
    a[x<<1|1].tag=min(tag,a[x<<1|1].tag);
}
void update(int x,int nl,int nr,int l,int r,int delta)
{
    ///if(nl!=nr)pushdown(x);
    if(l<=nl&&nr<=r)a[x].tag=min(a[x].tag,delta);
    else
    {
        //printf("%d %d\n",nl,nr);
        int mid=(nl+nr)>>1;
        if(l<=mid)update(x<<1,nl,mid,l,r,delta);
        if(r>mid)update(x<<1|1,mid+1,nr,l,r,delta);
    }
}
ll query(int x,int nl,int nr,int num)
{
    if(nl!=nr)pushdown(x);
    if(nl==num&&nr==num)return a[x].tag;
    else
    {
        int mid=(nl+nr)>>1;
        if(nl<=num&&num<=mid)return query(x<<1,nl,mid,num);
        if(mid<num&&num<=nr)return query(x<<1|1,mid+1,nr,num);
    }
}
void work(int x,int y,int len)
{
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2])swap(x,y),swap(f1,f2);
        update(1,1,cnt,w[f1],w[x],len);
        x=fa[f1];f1=top[x];
    }
    if(x==y)return;
    if(dep[x]<dep[y])swap(x,y);
    update(1,1,cnt,w[son[y]],w[x],len);
}
void build(int x,int l,int r)
{
    a[x].tag=inf;int mid=(l+r)>>1;
    if(l!=r)build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
int main()
{
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        read(x);read(y);read(z);read(flag);
        if(!flag)edge[++tot2].x=x,edge[tot2].y=y,edge[tot2].len=z;
        else add(x,y,z),add(y,x,z);
    }
    dfs1(1);dfs2(1,1);build(1,1,n);
    for(int i=1;i<=tot2;i++)
    work(edge[i].x,edge[i].y,edge[i].len+dep[edge[i].x]+dep[edge[i].y]);
    for(int i=2;i<=n;i++)
    {
        int ans=query(1,1,cnt,w[i]);
        if(ans!=inf)printf("%d ",ans-dep[i]);
        else printf("-1 ");
    }
    return 0;
}
View Code

並查集:排序

 

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
    while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();
    k*=f;
}
const int maxn=4010;
struct zs{int too,sum,pre;}e[500010];
struct poi{int x,y,len;}edge[500010];
int n,m,x,y,z,flag,tot,tot2;
int fq[maxn],fa[maxn],h[maxn],v[maxn],last[maxn];
void add(int x,int y,int z){e[++tot].too=y;e[tot].sum=z;e[tot].pre=last[x];last[x]=tot;}
void dfs(int x,int fa)
{
    for(int i=last[x];i;i=e[i].pre)
    if(e[i].too!=fa)h[e[i].too]=h[x]+e[i].sum,fq[e[i].too]=x,dfs(e[i].too,x);
}
bool cmp(poi a,poi b){return a.len<b.len;}
int gf(int x){return x==fa[x]?x:fa[x]=gf(fa[x]);}
int main()
{
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        read(x);read(y);read(z);read(flag);
        if(flag)add(x,y,z),add(y,x,z);
        else edge[++tot2].x=x,edge[tot2].y=y,edge[tot2].len=z;
    }
    dfs(1,0);
    for(int i=1;i<=tot2;i++)
    edge[i].len+=h[edge[i].x]+h[edge[i].y];
    sort(edge+1,edge+1+tot2,cmp);
    for(int i=1;i<=n;i++)
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=tot2;i++)
    {
        int x=gf(edge[i].x),y=gf(edge[i].y);
        while(x!=y)
        {
            if(h[x]<h[y])swap(x,y);
            if(!v[x])v[x]=i;
            x=fq[x]=gf(fq[x]);
        }
    }
    for(int i=2;i<=n;i++)
    if(v[i])printf("%d ",edge[v[i]].len-h[i]);
    else printf("-1 ");
}
View Code
相關文章
相關標籤/搜索