题意
SD0062号选手小Q同学为了偷到SDOI7012的试题,利用高超的黑客技术潜入了SDOI出题组的内联网的中央控制系统,然而这个内联网除了配备有中央控制系统,还为内联网中的每条单向网线设定了特殊的通信口令,这里通信口令是一个字符串,不同网线的口令可能不同。这让小Q同学感觉有些棘手, 不过这根本难不倒他,很快他就分析出了整个内联网的结构。
内联网中有n个节点(从1到n标号)和m条单向网线,中央控制系统在第1个节点上,每条网线单向连接内联网中的某两个节点,从1号节点出发经过若干条网线总能到达其他任意一个节点。每个节点都可以运行任意的应用程序,应用程序会携带一条通信口令,当且仅当程序的口令与网线的口令相同时,程序才能通过这条网线到达另一端的节点继续运行,并且通过每条网线都需要花费一定的时间。
每个应用程序可以在任意一个节点修改通信口令,修改通信口令花费的时间可以忽略不计,但是为了减小修改量,需要先调用一个子程序来计算当前程序的口令和网线的口令的最长公共前缀(记其长度为len,由于获取网线的口令的某个字符会比较耗时,调用一次这个子程序需要花费len个单位时间。
除此之外,小Q同学还在中央控制系统中发现了一个字典,每条网线的口令都是字典中的某个字符串。具体来说,这个字典是一棵k个节点(从1到k标号)的有根树,其中根是第1个节点,每条边上有一个字符,字符串S在字典中当且仅当存在某个点u使得从根节点出发往下走到u的这条路径上的字符顺次拼接构成S。
现在小Q同学在1号节点同时开启了n-1个应用程序,这些应用程序同时运行且互不干扰,每个程序的通信口令都为空,他希望用最短的时间把这些程序分别发送到其他节点上,你需要帮小 Q 同学分别计算出发送到第i(=2,3..n)个节点的程序完成任务的最短时间。
n,m<=50000,k<=20000
分析
首先我们把边看成点,把点看成边来建图。每条边拆成两个点连这条边的费用。对于每一个点,我们在它的所有入边和出边之间两两连边,费用为其lca深度。这样的话就可以直接跑最短路了。
这样的话显然不能过,考虑优化。
这里有两种做法:
一种是对于每个点,把和它相邻的每条边的节点拿出来建虚树,然后在dfs序上建线段树来优化连边。
想了想发现这实现起来是真的复杂,于是就去学习了一发Claris老师的做法。
假如现在有n个节点a[1..n]要两两求lca,我们将其按dfs序排序,设h[i]=dep[lca(a[i],a[i+1])],根据后缀数组height数组的性质不难得到dep[lca(a[i],a[j])]=min(h[i]..h[j-1])。
那么我们可以枚举中间的每个h[i],i两边的点就可以至少花费h[i]的费用来互相访问。我们只要建立前缀虚点和后缀虚电来优化连边即刻。
点数O(m),边数O(m),总复杂度O(mlogm),编程复杂度较低。
我居然在bzoj上跑了第二,仅次于Claris老师。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const LL inf=(LL)1e15;
const int N=50005;
int n,m,k,dep[N],dfn[N],size[N],fa[N],top[N],ls[N],in_h[N],out_h[N],in_nx[N],out_nx[N],pts[N];
int id1[N],id2[N],in_pre[N],out_pre[N],in_suf[N],out_suf[N],sz,last[N*10],cnt,tim,a[N];
struct edge{int to,next,w;}e[N*20];
LL dis[N*10];
bool vis[N*10],in[N],out[N];
priority_queue > que;
void clear()
{
memset(ls,0,sizeof(ls));
memset(in_h,0,sizeof(in_h));
memset(out_h,0,sizeof(out_h));
memset(last,0,sizeof(last));
cnt=tim=0;
}
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void addedge(int u,int v,int w,int op)
{
if (!op) e[++cnt].to=v,e[cnt].w=w,e[cnt].next=last[u],last[u]=cnt;
else e[++cnt].to=v,e[cnt].next=ls[u],ls[u]=cnt;
}
bool cmp(int x,int y) {return dfn[pts[x]]size[k]) k=e[i].to;
if (!k) return;
dfs2(k,chain);
for (int i=ls[x];i;i=e[i].next)
if (e[i].to!=k) dfs2(e[i].to,e[i].to);
}
int get_lca(int x,int y)
{
while (top[x]!=top[y])
{
if (dep[top[x]] u=que.top();que.pop();
while (!que.empty()&&vis[u.second]) u=que.top(),que.pop();
if (vis[u.second]) break;
int x=u.second;vis[x]=1;
for (int i=last[x];i;i=e[i].next)
if (dis[x]+e[i].w1) addedge(in_pre[i-1],in_pre[i],0,0),addedge(out_pre[i-1],out_pre[i],0,0);
}
for (int i=a1;i>=1;i--)
{
in_suf[i]=++sz;out_suf[i]=++sz;
if (in[a[i]]) addedge(id2[a[i]],in_suf[i],0,0);
if (out[a[i]]) addedge(out_suf[i],id1[a[i]],0,0);
if (i