标签: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;}
标签:swap href 题目 print c++ 区间 getchar lld return
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11791278.html