题目大意

给一张有向图,再给一颗字典树,有向图上的每条边有一个非负边权还有一个字典树上的字符串,从一条边到另一条边的代价是那条边的边权和这两个字符串的最长公共前缀,问从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;
}

[SDOI2017]天才黑客的更多相关文章

  1. [LOJ#2270][BZOJ4912][SDOI2017]天才黑客

    [LOJ#2270][BZOJ4912][SDOI2017]天才黑客 试题描述 SD0062 号选手小 Q 同学为了偷到 SDOI7012 的试题,利用高超的黑客技术潜入了 SDOI 出题组的内联网的 ...

  2. 【LG3783】[SDOI2017]天才黑客

    [LG3783][SDOI2017]天才黑客 题面 洛谷 题解 首先我们有一个非常显然的\(O(m^2)\)算法,就是将每条边看成点, 然后将每个点的所有入边和出边暴力连边跑最短路,我们想办法优化这里 ...

  3. Luogu P3783 [SDOI2017]天才黑客

    题目大意 一道码量直逼猪国杀的图论+数据结构题.我猪国杀也就一百来行 首先我们要看懂鬼畜的题意,发现其实就是在一个带权有向图上,每条边有一个字符串信息.让你找一个点出发到其它点的最短路径.听起来很简单 ...

  4. [SDOI2017]天才黑客[最短路、前缀优化建图]

    题意 一个 \(n\) 点 \(m\) 边的有向图,还有一棵 \(k\) 个节点的 trie ,每条边上有一个字符串,可以用 trie 的根到某个节点的路径来表示.每经过一条边,当前携带的字符串就会变 ...

  5. BZOJ4912 SDOI2017天才黑客(最短路+虚树)

    容易想到把边当成点重建图跑最短路.将每条边拆成入边和出边,作为新图中的两个点,由出边向入边连边权为原费用的边.对于原图中的每个点,考虑由其入边向出边连边.直接暴力两两连边当然会被卡掉,注意到其边权是t ...

  6. BZOJ4912 : [Sdoi2017]天才黑客

    建立新图,原图中每条边在新图中是点,点权为$w_i$,边权为两个字符串的LCP. 对字典树进行DFS,将每个点周围一圈边对应的字符串按DFS序从小到大排序. 根据后缀数组利用height数组求LCP的 ...

  7. BZOJ4912 [Sdoi2017]天才黑客 【虚树 + 最短路】

    题目链接 BZOJ4912 题解 转移的代价是存在于边和边之间的 所以把边看做点,跑最短路 但是这样做需要把同一个点的所有入边和所有出边之间连边 \(O(m^2)\)的连边无法接受 需要优化建图 膜一 ...

  8. bzoj 4912: [Sdoi2017]天才黑客

    Description Solution 这个题和点没什么关系 , 之和边与边之间关系有关 , 我们就把边看作点 , 边权就是 \(lcp\) , 点权看作这条边本来的权值. 现在考虑两两连边 , \ ...

  9. 洛谷P3783 [SDOI2017]天才黑客(前后缀优化建图+虚树+最短路)

    题面 传送门 题解 去看\(shadowice\)巨巨写得前后缀优化建图吧 话说我似乎连线段树优化建图的做法都不会 //minamoto #include<bits/stdc++.h> # ...

随机推荐

  1. .NET Core和.NET Standard有什么不同

        近日,微软发布了.NET Core 2.0,但是开发人员中间仍然存在一些疑惑,就是.NET Core..NET Standard.Xamarin和.NET Framework有什么不同. .N ...

  2. jQuery(四)、文档处理

    1 内部插入 1.1 append(content | fn) 向每个匹配的元素内部追加内容. 参数: (1) content:要追加到目标中的内容. (2) function(index, html ...

  3. MySQL优化面试

    原则:尽量使用整型表示字符串 存储IP INET_ATON(str),address to number INET_NTOA(number),number to address MySQL内部的枚举类 ...

  4. matlab中的实时音频

    音频系统工具箱™针对实时音频处理进行了优化.audioDeviceReader, audioDeviceWriter, audioPlayerRecorder, dsp.AudioFileReader ...

  5. 【English】十六、时间相关

    〇.其他 date: I have a date with her tomarrow. n.约会;日期,日子;时代,年代; vt.过时;使…显老;显示出…时代(或年龄);鉴定…的年代 vt.& ...

  6. DVWA 黑客攻防演练(十四)CSRF 攻击 Cross Site Request Forgery

    这么多攻击中,CSRF 攻击,全称是 Cross Site Request Forgery,翻译过来是跨站请求伪造可谓是最防不胜防之一.比如删除一篇文章,添加一笔钱之类,如果开发者是没有考虑到会被 C ...

  7. 快速排序实现及其pivot的选取

    coursera上斯坦福的算法专项在讲到快速排序时,称其为最优雅的算法之一.快速排序确实是一种比较有效的排序算法,很多类库中也都采用了这种排序算法,其最坏时间复杂度为$O(n^2)$,平均时间复杂度为 ...

  8. 关于ORACLE数据库名以及数据实例名等几个重要概念

    在Oracle中有关数据库和数据库实例的几个重要概念,有时候如果理解不是很深或者对其疏忽.混淆了,还真容易搞错或弄不清其概念,下面就数据库实例名.数据库名.数据库域名.数据库服务名.全局数据库名几个概 ...

  9. 数据库原理剖析 - 序列1 - B+树

    本文节选自<软件架构设计:大型网站技术架构与业务架构融合之道>第6.3章节. 作者微信公众号: 架构之道与术.进入后,可以加入书友群,与作者和其他读者进行深入讨论.也可以在京东.天猫上购买 ...

  10. ASP.NETMVC 分页

    <div class="text-center">    <span style="display:inline-block;  position:re ...