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

Luogu P3783 [SDOI2017]天才黑客

时间:2019-11-04 13:08:42      阅读:71      评论:0      收藏:0      [点我收藏+]

标签:swap   href   题目   print   c++   区间   getchar   lld   return   

题目
调了差不多有10h吧,真的我太难了。
首先一个比较自然的想法是化边为点,每条边拆成一个入点和一个出点,入点到出点连一条长度为这条边的边权的边。同时对于两条边而言,从各自的出点到对方的入点连一条长度为两条边的字符串的\(lcp\)的边。
这样建出来的边数是\(O(m^2)\)的,非常的不优秀。
我们可以发现一件事情:我们把所有字符串按其在trie上的dfs序排个序,设\(l_i=lcp(s_i,s_{i+1})=dep_{lca(s_i,s_j)}\),那么\(lcp(s_i,s_j)=\min\limits_{k\in[i,j)}l_k\)
那么一种可行的优化建图是利用单调栈求出各个\(l_i\)被取到的区间,然后线段树优化建图,边数为\(O(m\log^2m)\)
但是我们有更加优秀的优化建图的方法。
每个\(l_i\)代表\(\le i\)的出点可以通过\(l_i\)的代价走到\(>i\)的入点,\(>i\)的出点可以通过\(l_i\)的的代价走到\(\le i\)的入点。
直接连就是\(O(n^3)\)的了。
我们可以这样连:
\(i\)的入点和出点分别与\(i+1\)的入点和出点连一条\(0\)的边。
对于\(\le i\)的出点可以通过\(l_i\)的代价走到\(>i\)的入点,连\(i\)的出点到\(i+1\)的入点边权为\(l_i\)的边。
对于\(>i\)的出点可以通过\(l_i\)的的代价走到\(\le i\)的入点这种边,我们可以把\(i\)的入点和出点都拆成两个,一对处理上面那种,另一对处理下面这种。
所以另一对就这么连:
\(i\)的入点和出点分别与\(i-1\)的入点和出点连一条\(0\)的边。
对于\(>i\)的出点可以通过\(l_i\)的代价走到\(\le i\)的入点,连\(i+1\)的出点到\(i\)的入点边权为\(l_i\)的边。
那么每条边的两个入点到两个出点都要连长度为这条边边权的边。
最后建一个超级原点,到所有从\(1\)出发的边的入点连一条长度为\(0\)的边,跑一遍最短路。
对于每一个点在原图中的最短路,就是所有原图中指向它的点的出点的最短路。
码风比较清奇?凑合着看吧。

#include<bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define Pi pair<ll,int>
#define pb push_back
#define fi first
#define se second
using namespace std;
int read()
{int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
void swap(int &a,int &b)
{a^=b^=a^=b;}
const int N=20007;
int n,m,k,dfn[N],dep[N];
namespace trie
{
    int fa[N][18],T;vector<int>E[N];
    void clear()
    {for(int i=1;i<=k;++i)E[i].clear();memset(fa,0,sizeof fa),T=0;}
    void add(int w,int v,int u)
    {E[u].pb(v);}
    void dfs(int u)
    {for(int i=0;fa[u][i];++i)fa[u][i+1]=fa[fa[u][i]][i];dfn[u]=++T;for(int v:E[u])if(v^fa[u][0])dep[v]=dep[u]+1,fa[v][0]=u,dfs(v);}
    int lca(int u,int v)
    {if(dep[u]<dep[v])swap(u,v);for(int d=dep[u]-dep[v],i=0;d;d>>=1,++i)if(d&1)u=fa[u][i];if(u==v)return u;for(int i=17;~i;--i)if(fa[u][i]^fa[v][i])u=fa[u][i],v=fa[v][i];return fa[u][0];}
    void build()
    {dfs(1);}
}
namespace graph
{
    const int M=50007,V=M*4;
    int t,s,p[V],vis[V];ll dis[V];vector<pi>e[V];vector<int>a[M],b[M],c[M],d[M],vec;priority_queue<Pi>q;
    int cmp(int i,int j)
    {return dfn[p[i]]<dfn[p[j]];}
    void clear()
    {for(int i=1;i<=s+1;++i)e[i].clear(),a[i].clear(),b[i].clear(),c[i].clear(),d[i].clear();t=0;}
    void add(int u,int v,int w)
    {e[u].pb(pi(v,w));}
    void ins(int pos,int w,int v,int u)
    {
    if(u==1)add(s,t+1,0),add(s,t+3,0);for(int i=1;i<=4;i++)p[t+i]=pos; 
        add(t+1,t+2,w),add(t+1,t+4,w),add(t+3,t+4,w),add(t+3,t+2,w),a[u].pb(++t),b[v].pb(++t),c[u].pb(++t),d[v].pb(++t);
    }
    void build()
    {
    for(int i,j,k,u=1,w;u<=n;++u)
    {
        sort(a[u].begin(),a[u].end(),cmp),sort(b[u].begin(),b[u].end(),cmp),sort(c[u].begin(),c[u].end(),cmp),sort(d[u].begin(),d[u].end(),cmp);
        for(i=0;i+1<a[u].size();++i)add(a[u][i],a[u][i+1],0);
        for(i=0;i+1<b[u].size();++i)add(b[u][i],b[u][i+1],0);
        for(i=0;i+1<c[u].size();++i)add(c[u][i+1],c[u][i],0);
        for(i=0;i+1<d[u].size();++i)add(d[u][i+1],d[u][i],0);
        vec.resize(a[u].size()+b[u].size()),merge(b[u].begin(),b[u].end(),a[u].begin(),a[u].end(),vec.begin(),cmp);
        for(k=i=j=0;k+1<vec.size();++k)
        {
        (vec[k]&1?++j:++i),w=dep[trie::lca(p[vec[k]],p[vec[k+1]])];
        if(i&&j!=a[u].size()) add(b[u][i-1],a[u][j],w);
        if(i!=d[u].size()&&j) add(d[u][i],c[u][j-1],w);
        }
    }
    }
    void dij()
    {
    memset(dis,0x3f,sizeof dis),memset(vis,0,sizeof vis),q.push(Pi(dis[s]=0,s));
    for(int u;!q.empty();)
    {
        u=q.top().se,q.pop();if(vis[u])continue;vis[u]=1;
        for(auto[v,w]:e[u]) if(dis[v]>dis[u]+w)dis[v]=dis[u]+w,q.push(Pi(-dis[v],v));
    }
    }
    void solve()
    {for(int i=2;i<=n;++i){ll ans=1e18;for(int x:b[i])ans=min(ans,dis[x]);for(int x:d[i])ans=min(ans,dis[x]);printf("%lld\n",ans);}}
}
void solve()
{
    n=read(),m=read(),k=read(),graph::s=4*m+1,trie::clear(),graph::clear();
    for(int i=1;i<=m;++i)graph::ins(read(),read(),read(),read());
    for(int i=2;i<=k;++i)trie::add(read(),read(),read());
    trie::build(),graph::build(),graph::dij(),graph::solve();
}
int main()
{for(int T=read();T;--T)solve();return 0;}

Luogu P3783 [SDOI2017]天才黑客

标签:swap   href   题目   print   c++   区间   getchar   lld   return   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11791278.html

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