[SDOI2017]天才黑客

题目大意

给一张有向图,再给一颗字典树,有向图上的每条边有一个非负边权还有一个字典树上的字符串,从一条边到另一条边的代价是那条边的边权和这两个字符串的最长公共前缀,问从1到其他点的最短路、

题解

一看肯定是一个最短路问题,现在的关键问题是如何把这张图建出来。

我们可以枚举每个点作为两条边的中转点,然后直接把每条边看作一个点,对应的去连边复杂度肯定不对。

我们发现对于所有点,和它们相连的所有边的总和是\(O(m)\)的,所以我们考虑对每个点,对它相邻的所有边建一个虚树。

然后观察到两条边代表的字符串的最长公共前缀也是它们在字典树上的\(LCA\),所以我们在虚树上枚举\(LCA\),然后再去枚举进来的边,那么可以作为连出去的边在虚树上的\(dfs\)序是一段或两段连续的区间,然后再对\(dfs​\)序建线段树优化一下连边就可以了。

代码

写了大半天,自闭了。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<vector>
#define mm make_pair
#define P pair<int,int>
#define N 100009
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
priority_queue<pair<ll,int> >q;
int dfn[N],head[N*30],deep[N],p[20][N],tot,a[N],st[N],top,rbs[N],rot,num,_tag[N],tott,df[N],ddf,size[N],rt1,rt2,n,m,k;
ll dis[N*30];
bool vis[N*30];
vector<int>vec[N],ed[N],ru[N],chu[N],co1[N],co2[N];
vector<int>::iterator it;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
struct edge{int n,to,l;}e[N*30];
struct seg{int l,r;}tr[N*30];
inline void link2(int u,int v,int l){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;}
inline void link(int u,int v){ed[u].push_back(v);}
struct node{int a,b,c,d;}b[N];
inline void spfa(int s){
memset(dis,0x3f,sizeof(dis));
for(it=chu[s].begin();it!=chu[s].end();++it)dis[*it]=b[*it].c,q.push(mm(0,*it));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].n){
int v=e[i].to,x=v<=m?b[v].c:0;
if(dis[v]>dis[u]+e[i].l+x){
dis[v]=dis[u]+e[i].l+x;
q.push(mm(-dis[v],v));
}
}
}
}
void dfs(int u){
dfn[u]=++dfn[0];
for(int i=1;(1<<i)<=deep[u];++i)p[i][u]=p[i-1][p[i-1][u]];
for(vector<int>::iterator it=vec[u].begin();it!=vec[u].end();++it){
int v=*it;deep[v]=deep[u]+1;p[0][v]=u;
dfs(v);
}
}
inline int getlca(int u,int v){
if(deep[u]<deep[v])swap(u,v);
for(int i=19;i>=0;--i)if(deep[u]-(1<<i)>=deep[v])u=p[i][u];
if(u==v)return u;
for(int i=19;i>=0;--i)if(p[i][u]!=p[i][v])u=p[i][u],v=p[i][v];
return p[0][u];
}
inline void get_tree(){
sort(a+1,a+num+1);
num=unique(a+1,a+num+1)-a-1;
sort(a+1,a+num+1,cmp);
st[top=1]=a[1];rbs[rbs[0]=1]=a[1];rot=a[1];
for(int i=2;i<=num;++i){
if(st[top]==a[i])continue;
int x=a[i],lca=getlca(a[i],st[top]);
if(lca==st[top]){st[++top]=a[i];rbs[++rbs[0]]=a[i];continue;}
while(top>1){
int x=st[top],y=st[top-1];top--;
if(dfn[lca]>=dfn[y]){
link(lca,x);break;
}
link(y,x);
}
if(dfn[lca]<dfn[st[top]]){
link(lca,st[top]);top--;
if(dfn[lca]<dfn[rot])rot=lca;
}
if(st[top]!=lca){st[++top]=lca;rbs[++rbs[0]]=lca;}
if(st[top]!=x){st[++top]=x;rbs[++rbs[0]]=x;}
}
while(top>1){link(st[top-1],st[top]);top--;}
}
int build(int l,int r,int tag){
int cnt=++tott;tr[cnt].l=tr[cnt].r=0;
if(l==r){
int x=_tag[l];
if(!tag){
for(vector<int>::iterator it=co1[x].begin();it!=co1[x].end();++it)link2(*it,cnt,0);
}
if(tag){
for(vector<int>::iterator it=co2[x].begin();it!=co2[x].end();++it)link2(cnt,*it,0);
}
return cnt;
}
int mid=(l+r)>>1;
ls=build(l,mid,tag);rs=build(mid+1,r,tag);
if(!tag)link2(ls,cnt,0),link2(rs,cnt,0);
else link2(cnt,ls,0),link2(cnt,rs,0);
return cnt;
}
void upd(int cnt,int l,int r,int L,int R,int tag,int x,int len){
if(l>=L&&r<=R){
if(!tag)link2(cnt,x,len);
else link2(x,cnt,len);
return;
}
int mid=(l+r)>>1;
if(mid>=L)upd(ls,l,mid,L,R,tag,x,len);
if(mid<R)upd(rs,mid+1,r,L,R,tag,x,len);
}
void dfs2(int u){
df[u]=++ddf; _tag[ddf]=u;size[u]=1;
for(vector<int>::iterator it=ed[u].begin();it!=ed[u].end();++it){
int v=*it;
dfs2(v);
size[u]+=size[v];
}
}
void dfs3(int u){
int nw=df[u],en=df[u]+size[u]-1,sz=0;
int x=++tott;
upd(rt1,1,ddf,nw,nw,0,x,0);
upd(rt2,1,ddf,nw,en,1,x,deep[u]);
for(vector<int>::iterator it=ed[u].begin();it!=ed[u].end();++it){
int v=*it;
dfs3(v);x=++tott;
int L=nw+sz+1,R=nw+sz+size[v];
upd(rt1,1,ddf,L,R,0,x,0);
if(L>nw)upd(rt2,1,ddf,nw,L-1,1,x,deep[u]);
if(R<en)upd(rt2,1,ddf,R+1,en,1,x,deep[u]);
sz+=size[v];
}
}
inline void unit(){
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
memset(p,0,sizeof(p));
tot=0;
tott=0;dfn[0]=0;
for(int i=1;i<=n;++i){
ru[i].clear();chu[i].clear();
}
for(int i=1;i<=k;++i)vec[i].clear(); }
int main(){
int T=rd();
while(T--){
n=rd();m=rd();k=rd();
int u,v,w;
for(int i=1;i<=m;++i){
b[i].a=rd();b[i].b=rd();b[i].c=rd();b[i].d=rd();
ru[b[i].b].push_back(i);
chu[b[i].a].push_back(i);
}
tott=m;
for(int i=1;i<k;++i){
u=rd();v=rd();w=rd();
vec[u].push_back(v);
}
dfs(1);
for(int i=1;i<=n;++i){
if(ru[i].empty()||chu[i].empty())continue;
num=0;
for(it=ru[i].begin();it!=ru[i].end();++it)a[++num]=b[*it].d,co1[a[num]].push_back(*it);
for(it=chu[i].begin();it!=chu[i].end();++it)a[++num]=b[*it].d,co2[a[num]].push_back(*it);
get_tree();
dfs2(rot);
rt1=build(1,ddf,0);
rt2=build(1,ddf,1);
dfs3(rot);
for(int j=1;j<=num;++j)co1[a[j]].clear(),co2[a[j]].clear();
ddf=0;
while(rbs[0]){
int x=rbs[rbs[0]];
ed[x].clear();
rbs[0]--;
}
}
spfa(1);
for(int i=2;i<=n;++i){
ll ans=1e18;
for(it=ru[i].begin();it!=ru[i].end();++it)ans=min(ans,dis[*it]);
printf("%lld\n",ans);
}
unit();
}
return 0;
}
上一篇:PHP正则表达式屏蔽电话号码中间段


下一篇:mysql中sql语句中常见的group_concat()函数意思以及用法,oracle中与其一样的功能函数是wmsys.wm_concat()