[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> # ...
随机推荐
- bootstrap tooltips在 angularJS中的使用
使用bootstrap自带的提示控件,省去了不少事情 <div class="s2" ng-init="InitTooltip()"> <in ...
- c# Base64解密加密
private static string base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ...
- Django学习之六:Django 常用模块导入记忆
Django 常用模块导入记忆 django相关 1. urls相关操作 from django.urls import path, re_path, include from django.urls ...
- dedecms织梦上下页标签和CSS
模板中的分页部分源码: <div class="pagination-wrapper" style= "font-size:20pt;color:red;text- ...
- Python 使用python-kafka类库开发kafka生产者&消费者&客户端
使用python-kafka类库开发kafka生产者&消费者&客户端 By: 授客 QQ:1033553122 1.测试环境 python 3.4 zookeeper- ...
- jar包 pom
动态的web工程tomcat 自带jar包: jstl: taglibs-standard-impl-1.2.5.jar taglibs-standard-spec-1.2.5.jar //以下是 ...
- Linux常用命令--文件操作
常用Linux命令笔记(1) 1. 创建文件/文件夹 参考博客:https://www.cnblogs.com/lclq/p/5741852.html. 使用cat命令创建新文件: 输入命令 # ca ...
- Linux 下载包链接地址
Linux包下载链接地址: http://mirrors.sohu.com http://mirrors.163.com/
- MyDAL - .QueryListAsync() 使用
索引: 目录索引 一.API 列表 .QueryListAsync() .QueryListAsync<M>() 如: .QueryListAsync<AgentInventoryR ...
- MySQL 关于性能的参数配置梳理
以下List是我们常见的MySQL参数配置,这个参数对提高实例的性能大有裨益. 其中 建议设置值,仅供参考,需要根据自己的业务场景和硬件资源仔细推敲. 参数 设置说明 建议设置值 lower_case ...