• 【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;
    }
  • 相关阅读:
    Python_turtle绘图实例(持续更新)
    C++程序设计实验考试准备资料(2019级秋学期)
    利用next_permutation()实现全排列 完成 阮小二买彩票
    用埃氏算法来素数求和
    C++指针注意事项
    double与float的输入输出格式
    图片文件隐写术
    文件操作与隐写
    MFC 消息机制
    MFC应用中处理消息 创建窗口和会话框的顺序
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793268.html
Copyright © 2020-2023  润新知