题面传送门

神仙题一道。

首先注意到这里的贡献涉及到边的顺序,并且只与相邻的边是什么有关,因此不难想到一个做法——边转点,点转边,具体来说对于每条边 \(e\),我们将其拆成两个点 \(in_e,out_e\),并连边 \(in_e\to out_e\),权值为 \(c_e\),同时对于所有 \(b_i=a_j\) 的边 \(i,j\),连边 \(out_i\to in_j\),权值为 \(dep[\text{LCA}(d_i,d_j)]\),以及对于所有 \(a_i=1\) 的边连 \(S\to in_i\),权值为 \(0\) 的边,跑最短路,最后 \(ans_i=\min\limits_{b_e=i}dis_{out_e}\)。正确性显然。

然而该做法效率低下,边数最高可达到 \(\mathcal O(m^2)\),一脸过不去的亚子。于是又到了优化建图的时间了,一个 observation 是对于一个点 \(u\) 而言,我们在 \(b_i=a_j=u\) 的边 \(i,j\) 之间连边,这些边边权的种类是不会太多的,因为假设 \(S=\{d_i|b_i=u\lor a_i=u\}\),那么这些边权肯定只能是 \(S\) 当中某两个点的 \(\text{LCA}\) 的深度,根据虚树那一套理论,\(S\) 当中任意两点的 \(\text{LCA}\) 总共只有 \(\mathcal O(|S|)\) 种可能。而对于所有点 \(u\),它们对应 \(|S|\) 的总和为 \(\mathcal O(m)\) 级别,这个是在我们能接受的范围内的。因此考虑从这个性质入手优化这道题,我们将 \(S\) 当中的点按 DFS 序排个序,那么根据虚树中一个定理:对于待建立虚树的 \(m\) 个点 \(a_1,a_2,\cdots,a_m\),假设将其按照 DFS 序排序得到 \(b_1,b_2,\cdots,b_m\),那么记 \(h_i=dep[\text{LCA}(a_i,a_{i+1})]\),有 \(dep[\text{LCA}(b_l,b_r)]=\min\limits_{i=l}^{r-1}h_i\)(和 SA 有点像),因此考虑按照套路求出 \(h_i\),那么问题可以转化为:

  • 有一排 \(t\) 个点,每个点有两种类型 \(0/1\),每两个点中间写有一个数 \(h_i\),我们要从所有 \(0\) 点向 \(1\) 点连边,权值为它们之间数的最小值。

这个有一个显然的做法——对于某个 \(h_x\),满足 \(\min\limits_{i=l}^{r-1}h_i=h_x\) 的 \(l,r\) 肯定分别在一段区间内,单调栈求出对应的区间,线段树优化建图即可,但是这个做法多一个 \(\log\),不知道能不能通过,而且实现起来过于麻烦,因此我们还需考虑更简便做法。考虑最短路的一个性质,就是对于两点间如果有多条边的情况,那么显然只有权值最小的边是有用的,其他边可连可不连——连了也不影响你最短路的大小。因此考虑对于某个 \(h_i\),我们不维护什么单调栈,直接从 \([1,i]\) 中的 \(0\) 点向 \([i+1,t]\) 中的 \(1\) 点连边,\([i+1,t]\) 中的 \(0\) 权点向 \([1,i]\) 中的 \(1\) 点连边,根据上面的结论,对于某个 \(l,r\),只有 \([l,r)\) 中 \(h\) 的最小值(设为 \(h_x\))是有用的,其他边连上也没事,而显然在考虑 \(x\) 的贡献时,已经连了 \(l,r\) 之间的边,因此最短路跑出来依然是对的。考虑优化,注意到每次都是某个前缀向后缀,或者后缀向前缀连边,因此考虑前后缀优化建图,具体来说建立四排点——前缀 in,前缀 out,后缀 in,后缀 out,然后随便瞎连连即可,具体见代码。

时间复杂度 \(\mathcal O(Tm\log m)\),然鹅常数非常大,直接上天……

const int MAXN=5e4;
const int MAXK=2e4;
const int LOG_N=16;
const int MAXV=6e5+1;
const int MAXE=1e7;
int n,m,k,S=6e5+1,ncnt;
int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],val[MAXE+5],ec=0;
void adde(int u,int v,int w){
// printf("%d %d %d\n",u,v,w);
to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;
}
struct edge{int a,b,c,d;} e[MAXN+5];
vector<int> in[MAXN+5],out[MAXN+5],g[MAXK+5];
int dfn[MAXK+5],fa[MAXK+5][LOG_N+2],dep[MAXK+5],tim=0;
void dfs(int x=1,int f=0){
dfn[x]=++tim;fa[x][0]=f;
for(int y:g[x]) dep[y]=dep[x]+1,dfs(y,x);
}
int getlca(int x,int y){
if(dep[x]<dep[y]) x^=y^=x^=y;
for(int i=LOG_N;~i;i--) if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=LOG_N;~i;i--) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
bool cmp(pii x,pii y){return dfn[x.fi]<dfn[y.fi];}
ll dis[MAXV+5],ans[MAXN+5];
void clear(){
S=6e5+1;ncnt=0;memset(hd,0,sizeof(hd));ec=0;
memset(dfn,0,sizeof(dfn));memset(fa,0,sizeof(fa));
memset(dep,0,sizeof(dep));tim=0;
memset(dis,63,sizeof(dis));memset(ans,63,sizeof(ans));
for(int i=1;i<=MAXN;i++) in[i].clear(),out[i].clear();
for(int i=1;i<=MAXK;i++) g[i].clear();
}
void solve(){
scanf("%d%d%d",&n,&m,&k);clear();ncnt=m<<1;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&e[i].a,&e[i].b,&e[i].c,&e[i].d);
out[e[i].a].pb(i);in[e[i].b].pb(i);
}
for(int i=1,u,v;i<k;i++) scanf("%d%d%*d",&u,&v),g[u].pb(v);dfs(1,0);
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=k;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
for(int i=1;i<=m;i++) adde(i,i+m,e[i].c);
for(int i=1;i<=m;i++) if(e[i].a==1) adde(S,i,0);
for(int i=1;i<=n;i++){
vector<pii> ed;
for(int x:in[i]) ed.pb(mp(e[x].d,x+m));
for(int x:out[i]) ed.pb(mp(e[x].d,x));
sort(ed.begin(),ed.end(),cmp);
vector<int> h(ed.size(),0);
vector<int> pre_in(ed.size(),0),pre_out(ed.size(),0);
vector<int> suf_in(ed.size(),0),suf_out(ed.size(),0);
for(int j=0;j+1<ed.size();j++) h[j]=dep[getlca(ed[j].fi,ed[j+1].fi)];
// printf("node %d\n",i);
// for(int j=0;j<ed.size();j++) printf("pts %d %d\n",ed[j].fi,ed[j].se);
// for(int j=0;j+1<ed.size();j++) printf("%d\n",h[j]);
for(int j=0;j<ed.size();j++) pre_in[j]=++ncnt;
for(int j=0;j<ed.size();j++) pre_out[j]=++ncnt;
for(int j=0;j<ed.size();j++) suf_in[j]=++ncnt;
for(int j=0;j<ed.size();j++) suf_out[j]=++ncnt;
for(int j=0;j+1<ed.size();j++) adde(pre_in[j+1],pre_in[j],0);
for(int j=0;j+1<ed.size();j++) adde(pre_out[j],pre_out[j+1],0);
for(int j=0;j+1<ed.size();j++) adde(suf_in[j],suf_in[j+1],0);
for(int j=0;j+1<ed.size();j++) adde(suf_out[j+1],suf_out[j],0);
for(int j=0;j<ed.size();j++){
if(ed[j].se<=m) adde(suf_in[j],ed[j].se,0),adde(pre_in[j],ed[j].se,0);
else adde(ed[j].se,suf_out[j],0),adde(ed[j].se,pre_out[j],0);
}
for(int j=0;j+1<ed.size();j++){
adde(suf_out[j+1],pre_in[j],h[j]);
adde(pre_out[j],suf_in[j+1],h[j]);
}
} memset(dis,63,sizeof(dis));memset(ans,63,sizeof(ans));
priority_queue<pii,vector<pii>,greater<pii> > q;
q.push(mp(dis[S]=0,S));
while(!q.empty()){
pii p=q.top();q.pop();
int sum=p.fi,x=p.se;
if(dis[x]<sum) continue;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];
if(dis[y]>dis[x]+z) q.push(mp(dis[y]=dis[x]+z,y));
}
}
for(int i=1;i<=m;i++) chkmin(ans[e[i].b],dis[i+m]);
for(int i=2;i<=n;i++) printf("%lld\n",ans[i]);
}
int main(){int qu;scanf("%d",&qu);while(qu--) solve();return 0;}

洛谷 P3783 - [SDOI2017]天才黑客(前后缀优化建图)的更多相关文章

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

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

  2. Codeforces 587D - Duff in Mafia(2-SAT+前后缀优化建图)

    Codeforces 题面传送门 & 洛谷题面传送门 2-SAT hot tea. 首先一眼二分答案,我们二分答案 \(mid\),那么问题转化为,是否存在一个所有边权都 \(\le mid\ ...

  3. 【SDOI2017】天才黑客(前后缀优化建图 & 最短路)

    Description 给定一张有向图,\(n\) 个点,\(m\) 条边.第 \(i\) 条边上有一个边权 \(c_i\),以及一个字符串 \(s_i\). 其中字符串 \(s_1, s_2, \c ...

  4. 洛谷3783 SDOI2017 天才黑客(最短路+虚树+边转点+线段树优化建图)

    成功又一次自闭了 怕不是猪国杀之后最自闭的一次 一看到最短路径. 我们就能推测这应该是个最短路题 现在考虑怎么建图 根据题目的意思,我们可以发现,在本题中,边与边之间存在一些转换关系,但是点与点之间并 ...

  5. 洛谷P3588 [POI2015]PUS(线段树优化建图)

    题面 传送门 题解 先考虑暴力怎么做,我们把所有\(r-l+1-k\)中的点向\(x\)连有向边,表示\(x\)必须比它们大,那么如果这张图有环显然就无解了,否则的话我们跑一个多源最短路,每个点的\( ...

  6. 洛谷 P5331 - [SNOI2019]通信(CDQ 分治优化建图+费用流)

    题面传送门 首先熟悉网络流的同学应该能一眼看出此题的建模方法: 将每个点拆成两个点 \(in_i,out_i\),连一条 \(S\to in_i\),容量为 \(1\) 费用为 \(0\) 的边 连一 ...

  7. Luogu P3783 [SDOI2017]天才黑客

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

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

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

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

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

随机推荐

  1. Hadoop MapReduce 保姆级吐血宝典,学习与面试必读此文!

    Hadoop 涉及的知识点如下图所示,本文将逐一讲解: 本文档参考了关于 Hadoop 的官网及其他众多资料整理而成,为了整洁的排版及舒适的阅读,对于模糊不清晰的图片及黑白图片进行重新绘制成了高清彩图 ...

  2. Redis:学习笔记-02

    Redis:学习笔记-02 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 4. 事物 Redis 事务本 ...

  3. Java:String对象小记

    Java:String对象小记 对 Java 中的 String 对象,做一个微不足道的小小小小记 字节和字符的区别 字节 byte: 一个字节包含8个位(bit),因此byte的取值范围为-128~ ...

  4. filebeat收集日志到elsticsearch中并使用ingest node的pipeline处理

    filebeat收集日志到elsticsearch中 一.需求 二.实现 1.filebeat.yml 配置文件的编写 2.创建自定义的索引模板 3.加密连接到es用户的密码 1.创建keystore ...

  5. Noip模拟75 2021.10.12

    T1 如何优雅的送分 他说是送分题,我就刚,没刚出来,想到莫比乌斯容斥后就都没推出来 好吧还是不能被恶心的题目,挑衅的语言打乱做题节奏 于是这一场也就没了.... $F(i)$表示$i$的不同质因子集 ...

  6. Noip模拟12 2021.7.12

    T1 interval 亏得昨天晚上改掉了T3并且理解了单调栈,今天一扫这题目就知道要用啥了. 先预处理出以a[i]为最大值的最大左右区间.然后再将a[i]取%!!!是的,要不然会影响单调栈的使用.. ...

  7. 【做题记录】[NOIP2016 普及组] 魔法阵

    P2119 魔法阵 2016年普及组T4 题意: 给定一系列元素 \(\{X_i\}\) ,求满足以下不等式的每一个元素作为 \(a,b,c,d\) 的出现次数 . \[\begin{cases}X_ ...

  8. wpa_supplicant启动出错rfkill: Cannot open RFKILL control device

    在板子是调试网络,千辛万苦把wpa_supplicant及其依赖都移植编译进来了,在板子上调试启动的时候启动报错了 D/wpa_supplicant( 1152): wpa_supplicant v2 ...

  9. 通用 Makefile(及makefile中的notdir,wildcard和patsubst)

    notdir,wildcard和patsubst是makefile中几个有用的函数,以前没留意过makefile中函数的用法,今天稍微看看~ 1.makefile里的函数 makefile里的函数使用 ...

  10. cm0 逆向分析

    目录 cm0 逆向分析 前言 Strings工具复习 String工具使用说明 Strings工具解cm0题 cm0 逆向分析 前言 Emmmmm,我假装你看到这里已经学过了我的<恶意代码分析实 ...