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