[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;
}
[SDOI2017]天才黑客的更多相关文章
- [LOJ#2270][BZOJ4912][SDOI2017]天才黑客
[LOJ#2270][BZOJ4912][SDOI2017]天才黑客 试题描述 SD0062 号选手小 Q 同学为了偷到 SDOI7012 的试题,利用高超的黑客技术潜入了 SDOI 出题组的内联网的 ...
- 【LG3783】[SDOI2017]天才黑客
[LG3783][SDOI2017]天才黑客 题面 洛谷 题解 首先我们有一个非常显然的\(O(m^2)\)算法,就是将每条边看成点, 然后将每个点的所有入边和出边暴力连边跑最短路,我们想办法优化这里 ...
- Luogu P3783 [SDOI2017]天才黑客
题目大意 一道码量直逼猪国杀的图论+数据结构题.我猪国杀也就一百来行 首先我们要看懂鬼畜的题意,发现其实就是在一个带权有向图上,每条边有一个字符串信息.让你找一个点出发到其它点的最短路径.听起来很简单 ...
- [SDOI2017]天才黑客[最短路、前缀优化建图]
题意 一个 \(n\) 点 \(m\) 边的有向图,还有一棵 \(k\) 个节点的 trie ,每条边上有一个字符串,可以用 trie 的根到某个节点的路径来表示.每经过一条边,当前携带的字符串就会变 ...
- BZOJ4912 SDOI2017天才黑客(最短路+虚树)
容易想到把边当成点重建图跑最短路.将每条边拆成入边和出边,作为新图中的两个点,由出边向入边连边权为原费用的边.对于原图中的每个点,考虑由其入边向出边连边.直接暴力两两连边当然会被卡掉,注意到其边权是t ...
- BZOJ4912 : [Sdoi2017]天才黑客
建立新图,原图中每条边在新图中是点,点权为$w_i$,边权为两个字符串的LCP. 对字典树进行DFS,将每个点周围一圈边对应的字符串按DFS序从小到大排序. 根据后缀数组利用height数组求LCP的 ...
- BZOJ4912 [Sdoi2017]天才黑客 【虚树 + 最短路】
题目链接 BZOJ4912 题解 转移的代价是存在于边和边之间的 所以把边看做点,跑最短路 但是这样做需要把同一个点的所有入边和所有出边之间连边 \(O(m^2)\)的连边无法接受 需要优化建图 膜一 ...
- bzoj 4912: [Sdoi2017]天才黑客
Description Solution 这个题和点没什么关系 , 之和边与边之间关系有关 , 我们就把边看作点 , 边权就是 \(lcp\) , 点权看作这条边本来的权值. 现在考虑两两连边 , \ ...
- 洛谷P3783 [SDOI2017]天才黑客(前后缀优化建图+虚树+最短路)
题面 传送门 题解 去看\(shadowice\)巨巨写得前后缀优化建图吧 话说我似乎连线段树优化建图的做法都不会 //minamoto #include<bits/stdc++.h> # ...
随机推荐
- MVC bootstrap-table显示数据时显示No matching records found
问题:bootstrap-table加载数据不显示 @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layou ...
- WIN10安装不上IIS,使用IISExpress作为发布服务
[背景] 本人开发Win程序,需要调用网站资源作为Win程序的辅助功能,为此需要本地开发环境支持IIS.最近重装系统,VS安装完后,接着再安装IIS,可以在添加删除程序中反复尝试,均告安装失败提示.最 ...
- es6之三个点(...)扩展运算符
我们看一个语法,你就知道es6对我们码农多友好,毕竟世界在进步 let arr=[1,2,3,4,54,56] console.log(...arr) 结果是????? 没错 ...这个运算符就是把这 ...
- javascript面向对象习题答案
第二章 1.如果我们在控制台中执行下列语句,结果分别是什么?为什么? var a; typeof a; undefined > var s = '1s'; s++; NaN > !!&qu ...
- Gulp 前端优化
使用方法: 下载 node.js , https://nodejs.org/en/,并安装 msi 一下命令都属于 dos 命令 node -v,npm -v,检验是否下载成功(出现版本号) 将 np ...
- IM多类型holder封装
如标题,这是一个在列表多类型视图时的一个简化封装方法,减少多余代码,提高复用性,更好迭代扩展,先看视图列表效果图 GitHub:https://github.com/1024477951/Fragme ...
- HandlerThread原理分析
HandlerThread是一个内部拥有Handler和Looper的特殊Thread,可以方便地在子线程中处理消息. 简单使用 HandlerThread的使用比较简单. mHandlerThrea ...
- Glide的 java.lang.RuntimeException: Expected instanceof GlideModule, but found:X.GlideModule@2e4554f
问题一 在添加过混淆规则后,App打包的时候,发现报错了 java.lang.RuntimeException: Expected instanceof GlideModule, but found: ...
- Review: Basic Knowledge about JavaScript 1
JavaScript shanzm
- svn上传*.so文件
做移动开发,android里面需要用到第三方类库,设计"*.so"文件. svn无法提交,Eclipse里面的svn视图里面该文档无版本图标. 原因描述:svn忽略某些扩展名的文件 ...