优化建图

news/2024/10/2 1:34:42

\(2-SAT\) 时刷到的,发现好像一点不会,学习下。

1. 线段树优化建图

当一个点与一段区间连边时,暴力连是 \(O(n^2)\) 的。
因为线段树有一个肥肠优秀的性质,一个区间最多被分为 \(O(logn)\) 个节点。
so,我们可以把区间当成放到线段树上,这样是 \(O(nlogn)\) 的。
具体的,我们建立一个入线段树出线段树,初始都连长度为 \(0\) 的边。
这是图片
建图时根据区间建即可。

  • 当区间到区间建图时,可以建立一个 虚点,让 区间->虚点p->区间,复杂度不变,常数较大。

肥肠的神奇啊。

I CF786B Legacy

裸题,建树建边即可,详见代码

#include <bits/stdc++.h> 
using namespace std;
//线段树优化建图模版 
#define ll long long
#define pii pair<ll,ll>
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define inf 1e18
//神tm pair要开longlong!!! const int N = 2e6+10,K = 5e5;int n,q,s;
struct made{int ver,nx;ll ed;
}e[N<<1];
int hd[N],tot;
void add(int x,int y,ll z){tot++;e[tot].nx = hd[x],e[tot].ver = y,e[tot].ed = z,hd[x] = tot;
}int a[N];
void build(int p,int l,int r){if(l == r)return a[l] = p,void();add(p<<1,p,0),add(p<<1|1,p,0);add(p+K,(p<<1)+K,0),add(p+K,(p<<1|1)+K,0);int mid = l + r >> 1;build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}
void add(int p,int x,int L,int R,int l,int r,ll z,bool op){if(L <= l && r <= R)return op?add(a[x],p+K,z) : add(p,a[x]+K,z),void();int mid = l + r >> 1;if(L <= mid)add(p<<1,x,L,R,l,mid,z,op);//if(R > mid)add(p<<1|1,x,L,R,mid+1,r,z,op);
}//线段树建 入树(p) 与 出树(p+K)bool v[N];
ll d[N];
void dij(int u){memset(v,0,sizeof v);for(int i = 1;i <= 2*K;i++)d[i] = inf;priority_queue<pii,vector<pii>,greater<pii> >q;d[u] = 0;q.push(mp(0,u));while(!q.empty()){int x = q.top().se;q.pop();if(v[x])continue;v[x] = 1;for(int i = hd[x];i;i = e[i].nx){int y = e[i].ver;ll z = e[i].ed;if(d[y] > d[x] + z){d[y] = d[x] + z;q.push(mp(d[y],y));}}}
}//dijdij
int main(){scanf("%d%d%d",&n,&q,&s);build(1,1,n);for(int i = 1;i <= n;i++)add(a[i]+K,a[i],0);for(int i = 1;i <= q;i++){int op,x,y,l,r;ll z;scanf("%d%d",&op,&x);if(op == 1)scanf("%d%lld",&y,&z),add(a[x],a[y]+K,z);else if(op == 2)scanf("%d%d%lld",&l,&r,&z),add(1,x,l,r,1,n,z,1);else scanf("%d%d%lld",&l,&r,&z),add(1,x,l,r,1,n,z,0);}dij(a[s]);for(int i = 1;i <= n;i++){if(d[a[i]] == inf)printf("-1 ");else printf("%lld ",d[a[i]]);}printf("\n");return 0;}

II P6348 [PA2011] Journeys
裸题,只是区间向区间连边

代码

III P9520 [JOISC2022] 监狱
非常好的一道题。

我们可以发现一个性质

  • 如果一个方案满足条件,则必然存在一种情况使每个囚犯都独立的由起点走向终点。
  • 证明:如果一个囚犯走到一半,另一个囚犯再走这种方案满足条件,则两个囚犯独立走也一定满足条件。

所以每个囚犯只需自己走即可。
我们考虑囚犯的先后顺序,定义有向边 \((x,y)\) 代表 \(x\) 号囚犯需要第 \(y\) 号囚犯先走,才可以满足条件。
可以发现:

  • 一个囚犯 \(i\),若有囚犯 \(j\)起点在囚犯 \(i\) 的路径上时,需要连接 \(i \rightarrow j\)
  • 类似的,一个囚犯 \(i\),若有囚犯 \(j\)终点在囚犯 \(i\) 的路径上时,需要连接 \(j \rightarrow i\)

最后只需判断是否存在即可。

但是这样的边数为 \(O(n^2)\),考虑优化建图。
因为我们判环可以 \(O(m)\) 判环,所以可以直接 树剖加线段树优化建图,直接 \(O(nlogn^2)\)
能过。

不过在树上也可以用倍增优化建图前后缀优化建图
都可以把边数优化到 \(O(nlogn)\)

  • 倍增优化的常数有些大,导致其实跑的差不多快

代码(树剖+线段树)

2. 前后缀优化建图

当我们需要一个集合内的点互相连边时,可优化到 \(O(n)\)

I P6378 [PA2010] Riddle
可以发现,每条边至少选一个点,每个部分只能选一个点。

这是一个 \(2 - SAT\) 问题,只需要考虑 \(!p \rightarrow q\)\(p \rightarrow !q\) 即可。
但这样我们发现边数是 \(O(n^2)\) 的,用 \(tarjan\) 找环的复杂度也是 \(O(n^2)\) 的。

需要优化建图。
为了简化建边,我们需要一些多余的点,叫做虚点
我们可以首先把集合拉成一个序列,我们可以类似于前缀和一样建一个前缀节点,第 \(i\)前缀节点连着前 \(i\) 个点。
这样我们只需要多建 \(n\) 个点与 \(2n\) 条边就可完成建边。类似的,我们也同样建 \(n\)后缀节点
这样第 \(i\) 个点想要连接其他的点只需要连接第 \(i-1\)前缀节点与第 \(i+1\)后缀节点 两条边即可。

这样一共有 \(8n\) 条边,复杂度线性。
跑下 \(2-SAT\) 即可。

代码

II P5344 【XR-1】逛森林

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/29486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

[ISITDTU 2019]EasyPHP RCE异或限制

解决一个一直以来的问题,RCE的异或绕过问题。先了解下奇技淫巧吧--> https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html 接下来说说今天的问题,在做异或问题是发现许多payload都是(xxxxx)^(%ff....),这都是怎么来的呢。 现在说说吧,就比如拿_号来说…

Linux nohup 命令

Linux nohup 命令 应用场景 使用 PyCharm 连接服务器跑模型虽然很方便,但是如果遇到网络不佳、PyCharm出BUG、急需转移阵地等情况就只能中断训练,前面的全白跑了。 因此可以尝试直接在服务器上使用命令跑模型,这个命令好说,笨一点的方法直接抄用 PyCharm 运行时输出的命令嘛…

读《如何高效学习》[加] 斯科特扬 笔记

收获颇多,优化了个人笔记整理及日程安排的思维体系, 将各个经验之谈提炼为了逻辑性较好的系统框架。提升了个人学习和工作效率。序言第一部分 整体性学习策略 1 获取 (1)简化 (2)容量 (3)速度 2 理解 3 拓展 深度拓展 横向拓展 纵向拓展 4 纠错 5 应用 测试 信息结…

实现队列 栈 双端队列

以下都是用list来实现的实现Stack# Implement a Stack in Python class Stack(object):def __init__(self):self.items = []def is_empty(self):return self.items == []def push(self, item):self.items.append(item)def pop(self):return self.items.pop()def peek(self):ret…

uiautomator2使用方法

一.设备连接 1.usb单设备连接d = u2.connect()2.usb多设备连接d = u2.connect("90bf8faf") # 多台设备填写device即可3.wifi连接d = u2.connect("ip:proxy") # wifi连接设备adb使用wifi连接设备:https://www.cnblogs.com/lihongtaoya/p/17553171.html 二…

查看、删除数据库

#删除和查询数据库 #查看当前数据库服务器中的所有数据库 show databases #查看hsp_db01数据库的定义信息 show create database `hsp_db01` #在创建数据库,表的时候为了规避关键字,可以用``反引号解决 #删除数据库hsp_db01 drop database hsp_db01

element-ui使用el-date-picker日期组件常见场景

开始 最近一直在使用 element-ui中的日期组件。 所以想对日期组件常用的做一个简单的总结; 1.处理日期组件选择的时候面板联动问题 2.限制时间范围解除两个日期面板之间的联动 我们发现2个日期面板之间其实是有联动关系的; 开始时间面板和结束时间面板始终只能相邻; 不能出…

[鸟哥私房菜]4.首次登录与在线求助

第4章 首次登录与在线求助 4.1.3 X Window 与命令行模式的切换 通常我们称命令行界面为终端界面、Terminal 或 Console。Linux 默认的情况下会提供六个终端(Terminal)来让用户登录, 切换的方式为使用:[Ctrl] + [Alt] + [F1]~[F6] 的组合按钮。其中 [Ctrl] + [Alt] + [F1] 为…