题目描述:
n个点,m条单向边(可能有重边,自环),保证1号点可以到任意点,一棵大小为k的字典树,每条边对应一个字符串(字典树中的某个节点)和一个代价ci。从边u经过点x紧接着走边v会有额外代价|LCP(u对应的字符串,v对应的字符串)|,即u,v在字典树上的对应的点的lca的深度。
求1号点走到2,3…n号点的最小代价。
n
,
m
≤
50000
,
k
≤
20000
n,m\le50000,k\le20000
n,m≤50000,k≤20000
题目分析:
dalao题解,写得很清晰:
边化点,拆为入点和出点,原点周围的入边和出边拿出来按字典树上dfs序排序之后利用与后缀数组的height类似的性质变为 h i h_i hi左边所有出点向 h i h_i hi右边所有入点连边(还有右边向左边连边,所以出入点分别要拆2个,入点要向两个出点连边)。
还有另一种做法是将周围一圈边在字典树上的点拿出来建两棵虚树,一棵儿子向父亲连边,另一棵父亲向儿子连边,一个LCA提供的边权就让它在第一棵的儿子向第二棵的兄弟连边(同样可以前缀和后缀和优化,新建儿子个数个点表示前缀和/后缀和,前缀第i个点向前缀第i-1个点和第i个儿子连边,后缀同理),边本身的点作LCA的情况,就让边对应的另一棵树上的点直接连向它或它直接连向另一棵树上的点。
优化建图好题。
Code(第一种做法,注意一下总边数的范围和多测清空):
#include<bits/stdc++.h>
#define maxn 200005
#define maxm 1000005
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int T,n,m,k;
namespace Dic{
const int N = 20005, Log = 15;
int st[N<<1][Log+1],cnt,lg[N<<1]={-1},in[N];
vector<int>G[N];
void dfs(int u,int d){
st[in[u]=++cnt][0]=d;
for(int v: G[u]) dfs(v,d+1),st[++cnt][0]=d;
}
inline void init(){
for(int i=1;i<=k;i++) G[i].clear(); cnt=0;
for(int i=1,x,y,z;i<k;i++) read(x),read(y),read(z),G[x].push_back(y);
dfs(1,0);
for(int i=1;i<=cnt;i++) lg[i]=lg[i>>1]+1;
for(int j=1;j<=Log;j++)
for(int i=1;i+(1<<j)-1<=cnt;i++)
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int lcp(int x,int y){
if((x=in[x])>(y=in[y])) swap(x,y);
int k=lg[y-x+1];
return min(st[x][k],st[y-(1<<k)+1][k]);
}
}
//in1 m in2 2m out1 3m out2 4m
vector<int>in[maxn],out[maxn],vec;
int a[maxn];
int fir[maxn],nxt[maxm],to[maxm],w[maxm],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
#define ID(x) (x<=m?x:x-2*m)
bool cmp(int i,int j){return Dic::in[a[ID(i)]]<Dic::in[a[ID(j)]];}
typedef pair<LL,int> pli;
LL dis[maxn];
priority_queue<pli,vector<pli>,greater<pli> >q;
int main()
{
int x,y,z;
read(T);
while(T--){
read(n),read(m),read(k);
memset(fir,0,(4*m+1)<<2),tot=0;
for(int i=1;i<=n;i++) in[i].clear(),out[i].clear();
for(int i=1;i<=m;i++)
read(x),read(y),read(z),read(a[i]),in[y].push_back(2*m+i),out[x].push_back(i),
line(i,2*m+i,z),line(i,3*m+i,z),line(m+i,2*m+i,z),line(m+i,3*m+i,z);
Dic::init();
for(int u=1;u<=n;u++){
sort(in[u].begin(),in[u].end(),cmp);
sort(out[u].begin(),out[u].end(),cmp);
for(int i=in[u].size()-1;i>0;i--) line(in[u][i-1],in[u][i],0),line(m+in[u][i],m+in[u][i-1],0);
for(int i=out[u].size()-1;i>0;i--) line(out[u][i-1],out[u][i],0),line(m+out[u][i],m+out[u][i-1],0);
vec.resize(in[u].size()+out[u].size());
merge(in[u].begin(),in[u].end(),out[u].begin(),out[u].end(),vec.begin(),cmp);
for(int t=0,i=0,j=0;t<vec.size()-1;t++){
if(vec[t]<=m) j++; else i++;
int w=Dic::lcp(a[ID(vec[t])],a[ID(vec[t+1])]);
if(i&&j<out[u].size()) line(in[u][i-1],out[u][j],w);
if(i<in[u].size()&&j) line(m+in[u][i],m+out[u][j-1],w);
}
}
memset(dis,0x3f,(4*m+1)<<3);
for(int i:out[1]) q.push(pli(dis[i]=0,i));
while(!q.empty()){
LL d=q.top().first;int u=q.top().second;q.pop();
if(dis[u]!=d) continue;
for(int i=fir[u],v;i;i=nxt[i]) if(dis[v=to[i]]>d+w[i]) q.push(pli(dis[v]=d+w[i],v));
}
for(int i=2;i<=n;i++){
LL ret=1ll<<60;
for(int j:in[i]) ret=min(ret,dis[j]);
printf("%lld\n",ret);
}
}
}