【BZOJ4912】天才黑客(SDOI2017)-最短路+虚树+线段树优化建图

测试地址:天才黑客
做法:本题需要用到最短路+虚树+线段树优化建图。
本人好像使用了本题最经典,但是也最难写,时间复杂度最高,也很丑的做法。但是作为本人接触线段树优化建图这种方法的第一道题,本人还是十分坚强地写出了本人OI生涯中最长的一份代码(长达6.2KB)。
首先简化题意,题目给出一张有向图,每条边上有一个字符串,是一棵给定的trie中能匹配的一个串,一条路径的代价是:每条边的边权之和,加上相邻两条边的字符串的LCP长度之和,要求从点1开始的单源最短路。
首先不难发现,点在这道题中除了连通之外,没什么实质性的作用。因此我们把边化为点,如果能从一条边走到另一条边就连单向边,边权为两条边上字符串的LCP长度。为了表现原来它作为边的边权,需要拆点并在中间连一条对应权值的边。但是这样连边的话最多会连O(m2)条边,瞬间爆炸,因此需要考虑优化建图。
对于原图中的一个点,它会给进入它的边(简称入边)和从它出去的边(简称出边)提供一种中继。考虑对这些边上的字符串在trie中所代表的点建立一棵虚树,我们发现连边可以呈现这样一种特征:我们枚举产生贡献的LCA,那么这个点的子树与子树之间应该连边,这个点到它的子树、它的子树到这个点也需要连边,这个点自己到自己也需要连边。这些边直接连接还是有爆炸的可能,但我们发现,一棵子树在DFS序上就是一个区间,那么问题就转化成从区间向区间连边。这时候要解决问题就需要一种神奇的技巧——线段树优化建图。
我们知道,一个区间在线段树上就是O(logn)个点。但是只有一棵线段树肯定不行,需要用两棵线段树来分别处理入边和出边,称之为入线段树和出线段树。从一个区间向另一个区间连边,想到直接从入线段树的点向出线段树的点连边,但这样会连出O(log2n)条边,有点大,所以应该加一个辅助节点,然后从入线段树向辅助节点连边,再从辅助节点向出线段树连边。有了这些统一连边,怎样设计两棵线段树的结构才能自动形成对应的路径呢?显然,从一条边进入入线段树后,应该是从叶子节点向父亲寻找一个可行的区间向出线段树行走,所以入线段树就是从儿子向父亲连边。而走到出线段树之后,要向下走到某一个叶子节点,再出到某一个具体的点,因此出线段树应该是从父亲向儿子连边。
所以,原来的任意一条从一个点到另一个点的边,都可以通过从一个点,经过某一个中继点产生的入线段树和出线段树,再到另一个点的过程。因为总的边数只有m,所以建图建的点数是O(m)的,边数是O(mlogm)的。
然而这个模型和原来的题目还有一些区别。例如,原先我们需要求从1号点出发的单源最短路,而换到新的图后,好像没有一个固定的源点,所以我们造一个超级源点,向那些在原图中1号点的出边连边。而到达每个点的路径,现在终点也不统一了,因此我们应该对每个原图中的点再开一个附加点,然后对这个点产生的中继入线段树的每个叶子节点,从这些节点向附加点连边,这样到达附加点的路径就是所求的最短路径了。
于是我们就以O(mlog2m)的复杂度卡过了这一题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1000000000ll*1000000000ll;
int T,n,m,k,S,nowp,first[2000010],tot,d[50010];
int firstin[50010],firstout[50010],nextin[50010],nextout[50010];
int firsttrie[20010],tottrie,dep[20010],fa[20010][16]={0},pos[20010]={0},tim;
int p[80010],totp,firstimt[20010],totimt,st[20010],sttop;
int posimt[20010],posin[20010],posout[20010],inrt,outrt;
int firstsegin[50010],firstsegout[50010],nextsegin[50010],nextsegout[50010];
int ch[2000010][2];
struct edge
{
    int v,next;
    ll w;
}e[10000010],trie[20010],imt[20010];
struct state
{
    int v;
    ll val;
    bool operator < (state a) const
    {
        return val>a.val;
    }
};
priority_queue<state> Q;
ll dis[2000010];
bool vis[2000010];

void insert(int a,int b,ll w)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    e[tot].w=w;
    first[a]=tot;
}

void trieinsert(int a,int b)
{
    trie[++tottrie].v=b;
    trie[tottrie].next=firsttrie[a];
    firsttrie[a]=tottrie;
}

void imtinsert(int a,int b)
{
    imt[++totimt].v=b;
    imt[totimt].next=firstimt[a];
    firstimt[a]=totimt;
}

void dfs(int v)
{
    pos[v]=++tim;
    for(int i=firsttrie[v];i;i=trie[i].next)
    {
        dep[trie[i].v]=dep[v]+1;
        fa[trie[i].v][0]=v;
        dfs(trie[i].v);
    }
}

int lca(int a,int b)
{
    if (dep[a]<dep[b]) swap(a,b);
    for(int i=15;i>=0;i--)
        if (dep[fa[a][i]]>=dep[b])
            a=fa[a][i];
    if (a==b) return a;
    for(int i=15;i>=0;i--)
        if (fa[a][i]!=fa[b][i])
            a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}

bool cmp(int a,int b)
{
    return pos[a]<pos[b];
}

void buildimt()
{
    int newsiz;
    sort(p+1,p+totp+1,cmp);
    newsiz=totp;
    for(int i=1;i<totp;i++)
        p[++newsiz]=lca(p[i],p[i+1]);
    totp=newsiz;
    sort(p+1,p+totp+1,cmp);
    newsiz=0;
    for(int i=1;i<=totp;i++)
        if (i==1||p[i]!=p[i-1])
        {
            p[++newsiz]=p[i];
            firstimt[p[i]]=0;
        }
    totp=newsiz;

    sttop=totimt=0;
    for(int i=1;i<=totp;i++)
    {
        while(sttop&&lca(st[sttop],p[i])!=st[sttop])
            sttop--;
        if (sttop) imtinsert(st[sttop],p[i]);
        st[++sttop]=p[i];
    }
}

void dfsimt(int v)
{
    posimt[++tim]=v;
    posin[v]=tim;
    for(int i=firstimt[v];i;i=imt[i].next)
        dfsimt(imt[i].v);
    posout[v]=tim;
}

int buildtree(int l,int r,bool mode,int x)
{
    int v=++nowp;
    ch[v][0]=ch[v][1]=0;
    if (l==r)
    {
        if (!mode)
        {
            for(int i=firstsegin[posimt[l]];i;i=nextsegin[i])
                insert(m+i,v,0);
            insert(v,(m<<1)+x,0);
        }
        else
        {
            for(int i=firstsegout[posimt[l]];i;i=nextsegout[i])
                insert(v,i,0);
        }
        return v;
    }
    int mid=(l+r)>>1;
    ch[v][0]=buildtree(l,mid,mode,x);
    ch[v][1]=buildtree(mid+1,r,mode,x);
    if (!mode) insert(ch[v][0],v,0),insert(ch[v][1],v,0);
    else insert(v,ch[v][0],0),insert(v,ch[v][1],0);
    return v;
}

void link(int v,int l,int r,int s,int t,int x,ll w,bool mode)
{
    if (l>=s&&r<=t)
    {
        if (!mode) insert(v,x,w);
        else insert(x,v,0);
        return;
    }
    int mid=(l+r)>>1;
    if (s<=mid) link(ch[v][0],l,mid,s,t,x,w,mode);
    if (t>mid) link(ch[v][1],mid+1,r,s,t,x,w,mode);
}

void innerbuild(int v)
{
    int newpoint;
    if (firstimt[v])
    {
        newpoint=++nowp;
        link(inrt,1,totp,posin[v],posin[v],newpoint,(ll)dep[v],0);
        link(outrt,1,totp,posin[v]+1,posout[v],newpoint,(ll)dep[v],1);
        newpoint=++nowp;
        link(inrt,1,totp,posin[v]+1,posout[v],newpoint,(ll)dep[v],0);
        link(outrt,1,totp,posin[v],posin[v],newpoint,(ll)dep[v],1);
    }
    newpoint=++nowp;
    link(inrt,1,totp,posin[v],posin[v],newpoint,(ll)dep[v],0);
    link(outrt,1,totp,posin[v],posin[v],newpoint,(ll)dep[v],1);
    for(int i=firstimt[v];i;i=imt[i].next)
    {
        innerbuild(imt[i].v);
        int L=posin[imt[i].v],R=posout[imt[i].v];
        newpoint=++nowp;
        link(inrt,1,totp,L,R,newpoint,(ll)dep[v],0);
        if (L!=posin[v]+1) link(outrt,1,totp,posin[v]+1,L-1,newpoint,(ll)dep[v],1);
        if (R!=posout[v]) link(outrt,1,totp,R+1,posout[v],newpoint,(ll)dep[v],1);
    }
}

void dijkstra(int S)
{
    memset(vis,0,sizeof(vis));
    state nxt;
    for(int i=1;i<=nowp;i++)
        dis[i]=inf;
    dis[S]=0;
    nxt.v=S,nxt.val=0;
    while(!Q.empty()) Q.pop();
    Q.push(nxt);
    while(!Q.empty())
    {
        state now=Q.top();Q.pop();
        while (vis[now.v]&&!Q.empty())
            now=Q.top(),Q.pop();
        if (vis[now.v]&&Q.empty()) break;
        vis[now.v]=1;
        for(int i=first[now.v];i;i=e[i].next)
            if (dis[e[i].v]>dis[now.v]+e[i].w)
            {
                dis[e[i].v]=dis[now.v]+e[i].w;
                nxt.v=e[i].v;
                nxt.val=dis[e[i].v];
                Q.push(nxt);
            }
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(first,0,sizeof(first));
        memset(firsttrie,0,sizeof(firsttrie));
        tot=tottrie=0;
        memset(firstin,0,sizeof(firstin));
        memset(firstout,0,sizeof(firstout));
        memset(nextin,0,sizeof(nextin));
        memset(nextout,0,sizeof(nextout));
        memset(firstsegin,0,sizeof(firstsegin));
        memset(firstsegout,0,sizeof(firstsegout));
        memset(nextsegin,0,sizeof(nextsegin));
        memset(nextsegout,0,sizeof(nextsegout));

        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            ll c;
            scanf("%d%d",&a,&b);
            nextin[i]=firstin[b];
            firstin[b]=i;
            nextout[i]=firstout[a];
            firstout[a]=i;
            scanf("%lld",&c);
            insert(i,m+i,c);
            scanf("%d",&d[i]);
        }

        S=(m<<1)+n+1;
        nowp=S;
        for(int i=firstout[1];i;i=nextout[i])
            insert(S,i,0);

        for(int i=1;i<k;i++)
        {
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            trieinsert(a,b);
        }
        dep[0]=-1,dep[1]=0;
        fa[1][0]=0;
        tim=0;
        dfs(1);
        for(int i=1;i<=15;i++)
            for(int j=1;j<=k;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];

        for(int i=1;i<=n;i++)
        {
            totp=0;
            for(int j=firstin[i];j;j=nextin[j])
            {
                p[++totp]=d[j];
                nextsegin[j]=firstsegin[d[j]];
                firstsegin[d[j]]=j;
            }
            for(int j=firstout[i];j;j=nextout[j])
            {
                p[++totp]=d[j];
                nextsegout[j]=firstsegout[d[j]];
                firstsegout[d[j]]=j;
            }
            buildimt();

            if (totp)
            {
                tim=0;
                dfsimt(p[1]);
                inrt=buildtree(1,totp,0,i);
                outrt=buildtree(1,totp,1,i);
                innerbuild(p[1]);
            }

            for(int j=firstin[i];j;j=nextin[j])
            {
                firstsegin[d[j]]=0;
                nextsegin[j]=0;
            }
            for(int j=firstout[i];j;j=nextout[j])
            {
                firstsegout[d[j]]=0;
                nextsegout[j]=0;
            }
        }

        dijkstra(S);
        for(int i=2;i<=n;i++)
            printf("%lld
",dis[(m<<1)+i]);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793268.html