2-sat 问题 【例题 Flags(2-sat+线段树优化建图)】
序:
模拟赛考了一道 2-sat 问题。之前从来没听过……
考完才发现其实这个东东只要一个小小的 tarjan 求强连通分量就搞定了。
这个方法真是巧妙啊,拿来讲讲。
What is it? [・_・?]
这个算法是为了应对一些这样的条件:x1 与 x1 中至少有一个成立
算法:
既然是“2"-sat,那就要充分发挥 “two” 的优势,由于x1限制关系,有一些点我们选了后,有一些点就不能选了,那我们就只能选另一个点了满足 x2。
我们把 a 想它必须选的点 b 连有向边,这样就构成了一个图,判定的话,判断是否有两个不能同时存在的点在一个强连通分量中就好了!
这有一位大佬写的比我更好 OTZ
当然重点是讲模拟赛的题!
例题 Flags:
题目大意:数轴上有 n 个旗子,第 i 个可以插在坐标 xi 或者 yi,最大化两两旗子之间的最小距离。
题解:
我们可以二分答案,转化为一个 2-sat 问题。
二分最小距离 d,那么一个点 a 向左向右 d 的 b 点都不能选,那么选了 a 就必须选 b 的另一个位置,然后我们连边,最后判断是不是有一个点的两个位置是不是在一个强连通分量里,在则不能,否则可以。
这题还有一个点就是如果直接建边的话,就有 $n^2$ 条边,你会发现这题一个点的连边都是连续的,那就不妨线段树优化建图。具体思想戳这里。
CODE:
#include<iostream>
#include<cstdio>
#include<stack>
#include<algorithm>
#include<cstring>
using namespace std; int v[];
int n,x,y,tot=,h[];
int scc[],dfn[],low[],C,cnt;
bool vis[];
struct Edge{
int x,next;
}e[];
pair<int,int> a[];
stack<int> s; inline void add_edge(int x,int y){
e[++tot].x=y;
e[tot].next=h[x],h[x]=tot;
} void tarjan(int x){
dfn[x]=low[x]=++cnt;
s.push(x),vis[x]=true;
for(int i=h[x];~i;i=e[i].next){
if(!dfn[e[i].x]){
tarjan(e[i].x),low[x]=min(low[x],low[e[i].x]);
}else if(vis[e[i].x]){
low[x]=min(low[x],dfn[e[i].x]);
}
}
if(dfn[x]==low[x]){
C++;
int tmp;
for(;;){
tmp=s.top();
vis[tmp]=false,scc[tmp]=C;
s.pop();
if(tmp==x)break;
}
}
} void build(int o,int l,int r){
if(r-l==){
add_edge(o+n*,a[l].second^);
return;
}
add_edge(o+n*,(o<<)+n*);
add_edge(o+n*,(o<<|)+n*);
int mid=l+r>>;
build(o<<,l,mid),build(o<<|,mid,r);
} void link(int o,int l,int r,int x,int y,int a){
if(l>=x&&r<=y){
add_edge(a,o+n*);
return;
}
int mid=l+r>>;
if(x<mid)link(o<<,l,mid,x,y,a);
if(y>mid)link(o<<|,mid,r,x,y,a);
} inline pair<int,int> get(int x,int len){
int l=,r=x;
pair<int,int> ans;
while(l<r){
int mid=l+r>>;
if(a[x].first-a[mid].first<len)r=mid;
else l=mid+;
}
ans.first=l;
l=x,r=n*-;
while(l<r){
int mid=l+r+>>;
if(a[mid].first-a[x].first<len)l=mid;
else r=mid-;
}
ans.second=l;
return ans;
} inline bool check(int d){
memset(scc,,sizeof(scc));
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low));
memset(h,-,sizeof(h)),tot=;
build(,,n*);
for(int i=;i<n*;i++){
int id=a[i].second;
pair<int,int> x=get(i,d);
if(i<x.second)link(,,n*,i+,x.second+,id);
if(x.first<i)link(,,n*,x.first,i,id);
}
C=cnt=;
for(int i=;i<n*;i++)if(!dfn[i])tarjan(i);
for(int i=;i<n*;i++)
if(scc[a[i].second]==scc[a[i].second^])return false;
return true;
} int main(){
scanf("%d",&n);
for(int i=;i<n;i++){
scanf("%d%d",&x,&y);
a[*i+]=make_pair(x,*i+);
a[*i]=make_pair(y,*i);
}
sort(a,a+n*);
int l=,r=;
while(l<r){
int mid=l+r+>>;
if(check(mid))l=mid;
else r=mid-;
}
printf("%d",l);
}
2-sat 问题 【例题 Flags(2-sat+线段树优化建图)】的更多相关文章
- 【ARC069F】Flags 2-sat+线段树优化建图+二分
Description 数轴上有 n 个旗子,第 ii 个可以插在坐标 xi或者 yi,最大化两两旗子之间的最小距离. Input 第一行一个整数 N. 接下来 N 行每行两个整数 xi, ...
- BZOJ5017 [SNOI2017]炸弹 - 线段树优化建图+Tarjan
Solution 一个点向一个区间内的所有点连边, 可以用线段树优化建图来优化 : 前置技能传送门 然后就得到一个有向图, 一个联通块内的炸弹可以互相引爆, 所以进行缩点变成$DAG$ 然后拓扑排序. ...
- 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流
[BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- 【bzoj4699】树上的最短路(树剖+线段树优化建图)
题意 给你一棵 $n$ 个点 $n-1$ 条边的树,每条边有一个通过时间.此外有 $m$ 个传送条件 $(x_1,y_1,x_2,y_2,c)$,表示从 $x_1$ 到 $x_2$ 的简单路径上的点可 ...
- 【BZOJ4276】[ONTAK2015]Bajtman i Okrągły Robin 线段树优化建图+费用流
[BZOJ4276][ONTAK2015]Bajtman i Okrągły Robin Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1],[a[i]+1,a[i]+2 ...
- 【bzoj3073】[Pa2011]Journeys 线段树优化建图+堆优化Dijkstra
题目描述 Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N编号,但是他发现道路实在太多了,他要一条条建简直是不可能的!于是他以如下方式建造道路:(a, ...
- 【bzoj4383】[POI2015]Pustynia 线段树优化建图+差分约束系统+拓扑排序
题目描述 给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r- ...
- BZOJ_4276_[ONTAK2015]Bajtman i Okrągły Robin_线段树优化建图+最大费用最大流
BZOJ_4276_[ONTAK2015]Bajtman i Okrągły Robin_线段树优化建图+最大费用最大流 Description 有n个强盗,其中第i个强盗会在[a[i],a[i]+1 ...
随机推荐
- 第一单元OO总结
- Exchange邮箱搭建
出现的问题: System.Runtime.InteropServices.COMException(0x8004020F): The server rejected one or more reci ...
- ORACLE的SQL JOIN方式大全
ORACLE的SQL JOIN方式大全 在ORACLE数据库中,表与表之间的SQL JOIN方式有多种(不仅表与表,还可以表与视图.物化视图等联结),官方的解释如下所示 A join is a que ...
- 实现HTTP文件下载
[原文:http://www.jb51.net/article/89958.htm] HTTP实现文件下载时,只要在服务器设置好相关响应头,并使用二进制传输文件数据即可,而客户端(浏览器)会根据响应头 ...
- bash编程之循环控制:
bash编程之循环控制: for varName in LIST; do 循环体 done while CONDITION; do 循环体 done until CONDITION; do 循 ...
- Where art thou-freecodecamp算法题目
Where art thou 1.要求 写一个 function,它遍历一个对象数组(第一个参数)并返回一个包含相匹配的属性-值对(第二个参数)的所有对象的数组. 如果返回的数组中包含 source ...
- Linux性能检测常用的10个基本命令
检测性能的10个命令汇总 uptim dmesg | tail vmstat 1 mpstat -P ALL 1 pidstat 1 iostat -xz 1 free -m sar -n DEV 1 ...
- centOS下lnamp安装
首先安装apache,mysql ,最后安装php 1>apache安装 安装:yum install -y httpd 运行:/bin/systemctl start httpd.servic ...
- 【Python学习之七】递归——汉诺塔问题的算法理解
汉诺塔问题 汉诺塔的移动可以用递归函数非常简单地实现.请编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A.B.C中第1个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的 ...
- 【python】python环境搭建
本文主要用于记录python环境的搭建一些不常用的命令,其他的安装步骤在其他大师可以找到就不详细列出了.(注:以下操作都是在Ubuntu14.04LTS版本) 一.环境查询相关 1.查询当前pytho ...