码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P3783 [SDOI2017]天才黑客

时间:2018-12-31 17:27:35      阅读:155      评论:0      收藏:0      [点我收藏+]

标签:res   strong   friend   其它   重要   https   lse   wap   最短路   

题目大意

一道码量直逼猪国杀图论+数据结构题。我猪国杀也就一百来行

首先我们要看懂鬼畜的题意,发现其实就是在一个带权有向图上,每条边有一个字符串信息。让你找一个点出发到其它点的最短路径。听起来很简单,手速码完Dijkstra

然而这题中除了路径上的边权和还要加上去的就是一条路径上所有边代表的字符串的LCP,还有一点就是所有的字符串都是一棵字典树上的路径。

问题有点复杂啊,那我们慢慢分析。


化边为点

考虑到如果我们在跑最短路的时候记录每个点过来的状态就会不可避免的扩出\(O(n^2)\)条边,直接GG

仔细一想这道题最难的部分在哪?其实还是求LCP部分,所以显然边的重要性比点大,因此我们化边为点

具体的说,我们把原先的每条边拆成一个入点一个出点,在这两点之间连边表示原来的边权。

那么现在我们只需要在这些边化为的点直接连接边权为两字符串LCP的边即可(从一个出点连至一个入点)


构建虚树

那么现在我们就要找到原来每个点,将所有与其相连边所需要的节点(不管是进入还是出去)之间的边互相连接。

乍一看这样的点数多的爆炸,但是仔细一想,总点数再怎么多也不会超过\(2m\)啊!

所以我们对这些点构造一颗虚树(不会虚树的左转Luogu P2495 [SDOI2011]消耗战

此时虚树上的点就是它子树内的点(包括他本身)之间的LCA,所以我们只要在这些点对之间连边,边权为这个点的\(dep-1\)(减不减\(1\)根据个人写法不同)


优化建边

像前面说的那样,直接两两暴力连边边数还是平方级别的。

我们发现这些点都要经过LCA,所以可以考虑以LCA为中转点建虚点连边。(不会建虚点的左转Luogu P1983 车站分级

然后每次连边的边数是\(O(n)\)的,但是有由于有多个点,所以总的边数还是\(O(n^2)\)优化了半天却好似放屁

但是我们发现这个时候我们连的边相当于是从某个子树连到某个点,再从这个点连到某个子树

子树的一个经典性质,相信我不说大家都知道:DFS序连续。

所以问题变成区间向点,点向区间连边。这个由于我比较菜(不会前后缀优化建图),同时这题的数据范围不大,因此我们可以用最经典的线段树优化建图(不会的左转Luogu P3588 [POI2015]PUS

那么我们只需要同时维护一棵出线段树和一棵入线段树即可


总结&&CODE

最后问题回归到最短路问题,那么这题就被我们轻松艰难的切掉了。

考虑复杂度,由于有线段树的存在总边数是\(O(n\log n)\)的(认为\(n,m\)同阶)

算上Dijkstra的一个\(\log\)复杂度为\(O(n\log^2n)\)

千万注意码的时候要注意自己在干什么,不要不小心把自己码晕了。

附上200+的CODE

// luogu-judger-enable-o2
#include<cstdio>
#include<cctype>
#include<queue>
#include<vector>
#include<cstring>
#include<algorithm>
#define RI register int
#define pb push_back
#define Ms(f,x) memset(f,x,sizeof(f))
#define Tp template <typename T>
using namespace std;
typedef long long LL;
const int N=50005,K=20005;
int t,n,m,k,dep[K],dfn[K],rv[2][K],tot;
class FileInputOutput
{
    private:
        #define S 1<<21
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
        char Fin[S],Fout[S],*A,*B; int Ftop,pt[25];
    public:
        Tp inline void read(T &x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        Tp inline void write(T x)
        {
            if (!x) return (void)(pc('0'),pc('\n')); RI ptop=0;
            while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
        }
        inline void Fend(void)
        {
            fwrite(Fout,1,Ftop,stdout);
        }
        #undef S
        #undef tc
        #undef pc
}F;
inline bool cmp(int x,int y)
{
    return dfn[x]<dfn[y];
}
class LCA_Solver
{
    private:
        #define P 15
        struct edge
        {
            int to,nxt;
        }e[K]; int head[K],cnt,idx,anc[K][P],x,y,z;
        inline void add(int x,int y)
        {
            e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
        }
        inline void reset(int now)
        {
            for (RI i=0;i<P-1;++i) anc[now][i+1]=anc[anc[now][i]][i];
        }
        #define to e[i].to
        inline void DFS(int now,int fa)
        {
            dep[now]=dep[fa]+1; reset(now); dfn[now]=++idx;
            for (RI i=head[now];i;i=e[i].nxt) anc[to][0]=now,DFS(to,now);
        }
        #undef to
        inline void swap(int &x,int &y)
        {
            int t=x; x=y; y=t;
        }
    public:
        inline void init(void)
        {
            cnt=idx=0; Ms(head,0); for (RI i=1;i<k;++i)
            F.read(x),F.read(y),F.read(z),add(x,y); DFS(1,0);
        }
        inline int query(int x,int y)
        {
            RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
            if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
            for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
            x=anc[x][i],y=anc[y][i]; return anc[x][0];
        }
}L;
class Graph_Solver
{
    private:
        #define NS 1000005
        #define MS 4000005
        struct edge
        {
            int to,nxt,v;
        }e[MS]; int head[NS],cnt,pos[NS],st,a,b,d,pcnt,p[N<<1]; bool vis[NS]; LL dis[NS],c;
        int num[2][K<<2],stack[K],top,father[K],size[K],ndfn[K];;
        struct data
        {
            LL val; int id;
            inline data(LL Val=0,int Id=0) { val=Val; id=Id; }
            inline friend bool operator <(data A,data B)
            {
                return A.val>B.val;
            }
        }; priority_queue <data> small; vector <int> v[N][2];
        inline void add(int x,int y,int z)
        {
            e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
        }
        inline void build(int opt,int now,int l,int r)
        {
            num[opt][now]=++tot; if (l==r) return (void)(rv[opt][p[l]]=tot);
            int mid=l+r>>1; build(opt,now<<1,l,mid); build(opt,now<<1|1,mid+1,r);
            if (!opt) add(num[opt][now<<1],num[opt][now],0),add(num[opt][now<<1|1],num[opt][now],0);
            else add(num[opt][now],num[opt][now<<1],0),add(num[opt][now],num[opt][now<<1|1],0);
        }
        #define O beg,end,id,val
        inline void link(int opt,int now,int l,int r,int beg,int end,int id,int val)
        {
            if (l>=beg&&r<=end) { if (!opt) add(num[opt][now],id,val); else add(id,num[opt][now],val); return; }
            int mid=l+r>>1; if (beg<=mid) link(opt,now<<1,l,mid,O); if (end>mid) link(opt,now<<1|1,mid+1,r,O);
        }
        #undef O
        inline void VT_build(void)
        {
            RI i; father[stack[top=1]=p[1]]=0;
            int t=pcnt; for (i=2;i<=t;++i)
            {
                int fa=L.query(stack[top],p[i]);
                while (top&&dep[stack[top]]>dep[fa])
                {
                    if (top==1||dep[stack[top-1]]<=dep[fa])
                    father[stack[top]]=fa; --top;
                }
                if (fa!=stack[top]) father[fa]=stack[top],stack[++top]=fa,p[++pcnt]=fa;
                stack[++top]=p[i]; father[p[i]]=fa;
            }
            sort(p+1,p+pcnt+1,cmp); for (i=1;i<=pcnt;++i) size[p[i]]=1;
            for (i=pcnt;i;--i) size[father[p[i]]]+=size[p[i]],ndfn[p[i]]=i;
            for (build(0,1,1,pcnt),build(1,1,1,pcnt),i=1;i<=pcnt;++i)
            {
                int now=p[i]; ++tot; link(0,1,1,pcnt,i,i,tot,dep[now]-1);
                link(1,1,1,pcnt,i,i+size[now]-1,tot,0); int fa=father[now]; if (!fa) continue;
                ++tot; link(0,1,1,pcnt,i,i+size[now]-1,tot,dep[fa]-1);
                if (ndfn[fa]<=i-1) link(1,1,1,pcnt,ndfn[fa],i-1,tot,0);
                if (i+size[now]<=ndfn[fa]+size[fa]-1) link(1,1,1,pcnt,i+size[now],ndfn[fa]+size[fa]-1,tot,0);
            }
        }
        Tp inline void miner(T &x,T y)
        {
            if (y<x) x=y;
        }
    public:
        inline void init(void)
        {
            RI i; for (i=1;i<=n;++i) v[i][0].clear(),v[i][1].clear();
            for (cnt=tot=0,Ms(head,0),i=1;i<=m;++i)
            {
                F.read(a); F.read(b); F.read(c); F.read(d);
                pos[tot+1]=d; pos[tot+2]=d; v[a][1].pb(tot+1); v[b][0].pb(tot+2);
                add(tot+1,tot+2,c); tot+=2;
            }
            pos[st=++tot]=1; v[1][0].pb(st);
        }
        inline void build(void)
        {
            RI j,k; int lim; for (RI i=1;i<=n;++i)
            {
                for (pcnt=k=0;k<2;++k)
                for (lim=v[i][k].size(),j=0;j<lim;++j)
                p[++pcnt]=pos[v[i][k][j]];
                sort(p+1,p+pcnt+1); pcnt=unique(p+1,p+pcnt+1)-p-1;
                sort(p+1,p+pcnt+1,cmp); VT_build();
                for (k=0;k<2;++k) for (lim=v[i][k].size(),j=0;j<lim;++j)
                {
                    int t=v[i][k][j]; if (!k) add(t,rv[k][pos[t]],0); else add(rv[k][pos[t]],t,0);
                }
            }
        }
        #define to e[i].to
        inline void Dijkstra(void)
        {
            Ms(dis,63); Ms(vis,0); small.push(data(dis[st]=0,st));
            while (!small.empty())
            {
                int now=small.top().id; small.pop();
                if (vis[now]) continue; vis[now]=1;
                for (RI i=head[now];i;i=e[i].nxt)
                if (dis[to]>dis[now]+e[i].v)
                small.push(data(dis[to]=dis[now]+e[i].v,to));
            }
        }
        #undef to
        inline void print(void)
        {
            for (RI i=2;i<=n;++i)
            {
                int lim=v[i][0].size(); LL ans=1e18;
                for (RI j=0;j<lim;++j) miner(ans,dis[v[i][0][j]]);
                F.write(ans);
            }
        }
}G;
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    for (F.read(t);t;--t)
    {
        F.read(n); F.read(m); F.read(k); G.init();
        L.init(); G.build(); G.Dijkstra(); G.print();
    }
    return F.Fend(),0;
}

Luogu P3783 [SDOI2017]天才黑客

标签:res   strong   friend   其它   重要   https   lse   wap   最短路   

原文地址:https://www.cnblogs.com/cjjsb/p/10202170.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!