[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> # ...
随机推荐
- 在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用
问题描述 在传统的基于 .NET Framework 的 WPF 程序中,我们可以使用如下代码段启动相关的默认应用: # 启动默认文本编辑器打开 helloworld.txt Process.Star ...
- flex 输入框布局
1:创建一个弹性容器(display:flex) 2:构建2个或3个弹性项目. 3:把弹性项目设置为居中对齐.(align-items:center) 4:改变input自身对齐方式,把它设置为拉伸以 ...
- .Net Core3 新特性/新功能 16条
.net core 3实现了.net 标准2.1. 1.生成可执行文件 以前版本需要dotnet run运行项目,.net core 3支持直接生成目标平台的可执行文件.比如windows就是exe了 ...
- [20190423]那个更快的疑问3.txt
[20190423]那个更快的疑问3.txt --//前一阵子,做了11g在单表单条记录唯一索引扫描的测试,摘要如下:--//参考链接:http://blog.itpub.net/267265/vie ...
- SQLServer之锁简介
锁定义(Definition) 锁定是 DBMS 将访问限制为多用户环境中的行的过程. 以独占方式锁定行或列,不允许其他用户访问锁定的数据,直到锁被释放. 这可确保两个用户不能同时更新行中的同一列. ...
- js坚持不懈之15:修改html内容和属性的方法
1. 修改 HTML 内容 <!DOCTYPE html> <html> <body> <p id = "change">原始内容& ...
- SSH服务与tcp wrappers实验
SSH服务与tcp wrappers实验 实验环境: 一台linux(ssh client) 一台linux(ssh server) 实验步骤: 1.配置IP,测试连通性 2.在客户端创建用户yuzl ...
- 如莲开发平台(MIS基础框架、Java技术、B/S结构)
关于 「如莲」是一套MIS类系统基础框架,主要用于各类“管理信息系统”的开发,也适合做网站后台开发.可省去开发时的框架搭建.规范约定.权限管理等基础工作,直接专注于业务功能实现. 「如 ...
- 初窥css---选择器及相关特性
选择器及相关特性 基础选择器 标签选择器 相当于全选,在我看来局限性较大,也没啥意义的感觉,用处不太大 id选择器 有利于对于某个小盒子的部分属性进行改变,但是若是需要改的小盒子很多的话,就会很麻烦 ...
- 【html】使用img标签和背景图片之间的区别
1.加载问题 背景图片会等到html结构加载完成才开始加载 img标签是网页结构的一部分,会在html结构加载的时候加载 在网页加载的过程中,背景图片会等到结构加载完成(网页的内容全部显示以后)才开始 ...