BZOJ 4912: [Sdoi2017]天才黑客 前缀和优化建图最短路
title
Description
内联网中有 \(n\) 个节点(从 \(1\) 到 \(n\) 标号)和 \(m\) 条单向网线,中央控制系统在第 \(1\) 个节点上,每条网线单向连接内联网中的某两个节点,从 \(1\) 号节点出发经过若干条网线总能到达其他任意一个节点。每个节点都可以运行任意的应用程序,应用程序会携带一条通信口令,当且仅当程序的口令与网线的口令相同时,程序才能通过这条网线到达另一端的节点继续运行,并且通过每条网线都需要花费一定的时间。
每个应用程序可以在任意一个节点修改通信口令,修改通信口令花费的时间可以忽略不计,但是为了减小修改量,需要先调用一个子程序来计算当前程序的口令和网线的口令的最长公共前缀(记其长度为 \(len\) ),由于获取网线的口令的某个字符会比较耗时,调用一次这个子程序需要花费 \(len\) 个单位时间。
除此之外,小Q同学还在中央控制系统中发现了一个字典,每条网线的口令都是字典中的某个字符串。具体来说,这个字典是一棵 \(k\) 个节点(从 \(1\) 到 \(k\) 标号)的有根树,其中根是第 \(1\) 个节点,每条边上有一个字符,字符串 \(S\) 在字典中当且仅当存在某个点 \(u\) 使得从根节点出发往下走到 \(u\) 的这条路径上的字符顺次拼接构成 \(S\) 。
现在小Q同学在 \(1\) 号节点同时开启了 \(n-1\) 个应用程序,这些应用程序同时运行且互不干扰,每个程序的通信口令都为空,他希望用最短的时间把这些程序分别发送到其他节点上,你需要帮小Q同学分别计算出发送到第 \(i(=2,3,\dots ,n)\) 个节点的程序完成任务的最短时间。
analysis
参考了 dsfr 的 \(blog\)。
首先我们把边看成点,把点看成边来建图。每条边拆成两个点连这条边的费用。
对于每一个点,我们在它的所有入边和出边之间两两连边,费用为其 \(lca\) 深度。
这样的话就可以直接跑最短路了。
这样的话显然不能过,考虑优化。
这里有两种做法:
第一种是对于每个点,把和它相邻的每条边的节点拿出来建虚树,然后在 \(dfs\) 序上建线段树来优化连边。
- 第二种:
- 假如现在有 \(n\) 个节点 \(a[1..n]\) 要两两求 \(lca\),我们将其按 \(dfs\) 序排序,设 \(h[i]=dep[lca(a[i],a[i+1])]\),根据后缀数组 \(height\) 数组的性质不难得到 \(dep[lca(a[i],a[j])]=min(h[i]..h[j-1])\)。
- 那么我们可以枚举中间的每个 \(h[i]\),\(i\) 两边的点就可以至少花费 \(h[i]\) 的费用来互相访问。我们只要建立前缀虚点和后缀虚点来优化连边即刻。 点数 \(O(m)\),边数 \(O(m)\),总复杂度 \(O(mlogm)\)。
code
#include<bits/stdc++.h>
#define mp std::make_pair
typedef std::pair<int,int> pii;
typedef long long ll;
const int maxn=5e4+10,maxm=maxn*10;
const ll inf=1ll<<60;
typedef int iarr[maxn];
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }
template<typename T>inline T min(T a,T b) { return a<b ? a : b; }
template<typename T>inline T max(T a,T b) { return a>b ? a : b; }
struct Graph
{
int ver[maxm<<1],edge[maxm<<1],Next[maxm<<1],head[maxm],len;
inline void Clear()
{
memset(head,0,sizeof(head));
len=0;
}
inline void add(int x,int y,int z)
{
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
}G, A;
iarr id1,id2,pts;
namespace lca
{
int dfn[maxn],id;
int dep[maxn],f[maxn][21];
inline void dfs(int x)
{
dfn[x]=++id;
for (int i=1; i<=20; ++i) f[x][i]=f[f[x][i-1]][i-1];
for (int i=A.head[x]; i; i=A.Next[i])
{
int y=A.ver[i];
if (y==f[x][0]) continue;
f[y][0]=x;
dep[y]=dep[x]+1;
dfs(y);
}
}
inline int LCA(int x,int y)
{
if (dep[x]>dep[y]) std::swap(x,y);
for (int i=20; i>=0; --i)
if (dep[y]-(1<<i)>=dep[x]) y=f[y][i];
if (x==y) return x;
for (int i=20; i>=0; --i)
if (f[x][i]^f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
inline bool cmp(int a,int b)
{
return dfn[pts[a]]<dfn[pts[b]];
}
}
using lca::dfs;
using lca::LCA;
using lca::cmp;
ll dist[maxm];
bool vis[maxm];
int a[maxn],a1,tot;
inline void Dijkstra(int s)
{
for (int i=0; i<=tot; ++i) dist[i]=inf,vis[i]=0;
std::priority_queue<pii,std::vector<pii>,std::greater<pii> >q;
q.push(mp(0,s));dist[s]=0;
while (!q.empty())
{
int x=q.top().second;
q.pop();
if (vis[x]) continue;
vis[x]=1;
for (int i=G.head[x]; i; i=G.Next[i])
{
int y=G.ver[i], z=G.edge[i];
if (chkMin(dist[y],dist[x]+z)) q.push(mp(dist[y],y));
}
}
}
iarr In, InNext, InHead, InPre, InSuf;
iarr Out,OutNext,OutHead,OutPre,OutSuf;
inline void Clear()
{
memset(InHead,0,sizeof(InHead));
memset(OutHead,0,sizeof(OutHead));
G.Clear(), A.Clear();
}
int main()
{
int T;read(T);
while (T--)
{
Clear();
int n,m,k;
read(n);read(m);read(k);
tot=m<<1;
for (int i=1,x,y,z; i<=m; ++i)
{
read(x),read(y),read(z),read(pts[i]);
id1[i]=(i<<1)-1,id2[i]=i<<1;
G.add(id1[i],id2[i],z);
InNext[i]=InHead[y], InHead[y]=i;
OutNext[i]=OutHead[x], OutHead[x]=i;
}
for (int i=1,x,y,z; i<k; ++i) read(x),read(y),read(z),A.add(x,y,z);
dfs(1);
for (int x=1; x<=n; ++x)
{
int a1=0;
for (int i=InHead[x]; i; i=InNext[i]) a[++a1]=i,In[i]=1;
for (int i=OutHead[x]; i; i=OutNext[i]) a[++a1]=i,Out[i]=1;
std::sort(a+1,a+a1+1,cmp);
for (int i=1; i<=a1; ++i)
{
InPre[i]=++tot, OutPre[i]=++tot;
if (In[a[i]]) G.add(id2[a[i]],InPre[i],0);
if (Out[a[i]]) G.add(OutPre[i],id1[a[i]],0);
if (i>1) G.add(InPre[i-1],InPre[i],0),G.add(OutPre[i-1],OutPre[i],0);
}
for (int i=a1; i>=1; --i)
{
InSuf[i]=++tot, OutSuf[i]=++tot;
if (In[a[i]]) G.add(id2[a[i]],InSuf[i],0);
if (Out[a[i]]) G.add(OutSuf[i],id1[a[i]],0);
if (i<a1) G.add(InSuf[i+1],InSuf[i],0),G.add(OutSuf[i+1],OutSuf[i],0);
}
for (int i=1; i<a1; ++i)
{
int d=lca::dep[LCA(pts[a[i]],pts[a[i+1]])];
G.add(InPre[i],OutPre[i+1],d);
G.add(InSuf[i+1],OutSuf[i],d);
}
for (int i=1; i<=a1; ++i) In[a[i]]=Out[a[i]]=0;
}
for (int i=OutHead[1]; i; i=OutNext[i]) G.add(0,id1[i],0);
Dijkstra(0);
for (int x=2; x<=n; ++x)
{
ll ans=inf;
for (int i=InHead[x]; i; i=InNext[i]) ans=min(ans,dist[id2[i]]);
write(ans,'\n');
}
}
IO::flush();
return 0;
}
原文地址:https://www.cnblogs.com/G-hsm/p/11405825.html
- C#新功能--命名参数与可选参数
- 这或许是对小白最友好的python入门了吧——14,遍历字典
- C#新功能--命名参数
- Tomcat 安全配置与性能优化
- 【机器学习】伪标签(Pseudo-Labelling)的介绍:一种半监督机器学习技术
- 这或许是对小白最友好的python入门了吧——13,字典初识
- PHP 安全与性能
- 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该...
- Linux 系统与数据库安全
- 这或许是对小白最友好的python入门了吧——12,列表深入体验
- 初识SqlLite ---.net连接数据库
- 【实践操作】:六步教你如何用开源框架Tensorflow对象检测API构建一个玩具检测器
- 这或许是对小白最友好的python入门了吧——11,if语句初体验
- 你的网站做好神马搜索优化了吗?神马搜索站长工具提交
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode之找不同
- 太强了,这居然是19年双非本科开发一年的Android面筋!开发几年的老程序员自叹不如
- Kotlin Vocabulary | Reified: 类型擦除后再生计划
- WorkManager 在 Kotlin 中的实践
- ElasticSearch 集群分片内部原理
- 360webscan防注入脚本全面绕过
- JQuery 入门学习(完结)
- 由python端口转发脚本看asyncore模块
- emlog模板制作说明(一)
- JQuery 入门学习(二)
- JQuery 入门学习(一)
- gh0st源码分析与远控的编写(二)
- struts2 S2-016/S2-017 Python GetShell
- 谈Python多线程及程序锁
- sqlite3的C语言使用(一)