// // // // // // // // // // // // // //

腾飞营_图论

图论

芝士

欧拉路径

经过每条边恰好一次的路径

欧拉回路: 闭合的欧拉路径

无向图存在欧拉回路的充要条件: 度数都为偶数的连通图

强联通分量

有向图中互相可达的极大子图

边双联通分量

无向图中边联通度大于等于 \(2\) 的极大子图

点双联通分量

无向图中点联通度大于等于 \(2\) 的极大子图

割点

一个点是割点当且仅当这个点在多个点双中

割边

一条边是割边当且仅当这条边不在任何一个边双

最短路

  • spfa \(O(nm)\)

  • dijkstra \(O((n + m)\log n)\)

  • floyed \(O(n^3)\)

    求最小环 ... 木有听懂...

差分约束系统

给定 \(n\) 个变量 \(x_i\) \(m\) 个约束条件 形如 \(x_i - x_j \leq k\) 判断是否可行 或者最优化某个值

将变量化为 \(x_k - k \leq x_j\) 的形式 视为一条 \(i \to j\) 的边 权值为 \(-k\) 若存在可行解则无负权回路

最小生成树

  • kruskal \(O(m\log m)\)
  • prim \(O(n^2)\)

2 - sat

给定 \(n\)\(bool\) 变量 给出 \(m\) 个二元限制 求一组可行解

对于每个变量 \(x\) 建立 \(x_0, x_1\) 两个点分别表示选或不选

\(a\)\(-a\) 在同一强联通分量中 则无解 否则可以拓扑排序输出一组解

Hall 定理

设二分图中两边的集合分别为 \(x, y\) 如果 \(x\) 中的任意 \(k\) 个点至少与 \(y\)

...

题目

AT2336 Flags

二分答案 + 2-set + 线段树优化建图

也可以分块的 但是我不会

最小距离最大 二分最小距离 两个可以放置的位置中只能取一个 建立 2-set 模型

建边复杂度过高 由点向区间建边 线段树优化

注意 check 的时候数组要清空 以及数组要开的足够大

/*
  Source: AT2336 [ARC069D] Flags
  最小值最大 二分最小距离 2-set 判断是否合法 
  建边复杂度过高 由点向区间建边 线段树优化建图  
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define emm(x) memset(x, 0, sizeof x)
#define Min(x, y) ((x) < (y) ? (x) : (y))
/*----------------------------------------------------------*/
const int B = 3e6 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen(".in", "r", stdin);
	freopen(".out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, acnt, ans, tcnt, rt;
int kcnt, st[B], top, num[B], scnt, dfn[B], low[B];
struct Node {int x, id;} a[B << 1];
struct edge {int v, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
bool cmp(Node x, Node y) {return x.x < y.x;}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r, id;} t[B << 2];
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r;
		if(l == r) {t[p].id = a[l].id + (n << 1); return ;}
		t[p].id = ++tcnt; build(ls(p), l, mid); build(rs(p), mid + 1, r);
		add_edge(t[p].id, t[ls(p)].id); add_edge(t[p].id, t[rs(p)].id);
	}
	void link(int p, int l, int r, int f) {
		if(l <= t[p].l && t[p].r <= r) {
		add_edge(f, t[p].id); 
		return ;}
		if(l <= mid) link(ls(p), l, r, f); if(r > mid) link(rs(p), l, r, f);
	}
	#undef mid
}
void dfs(int u) {
	dfn[u] = low[u] = ++kcnt; st[++top] = u;
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v;
		if(!dfn[v]) dfs(v), low[u] = Min(low[u], low[v]);
		else if(!num[v]) low[u] = Min(low[u], dfn[v]);
	}
	if(dfn[u] == low[u])
	{
		num[u] = ++scnt;
		while(st[top] != u) num[st[top--]] = scnt;
		top--;
	}
}
bool check(int x) {
	ecnt = kcnt = scnt = 0; emm(head); emm(dfn); emm(low); emm(num);
	tcnt = n << 2; Seg::build(1, 1, n << 1);
	for(int i = 1; i ^ n + 1; i++)
		add_edge(i, 3 * n + i), add_edge(3 * n + i, i),
		add_edge(n + i, 2 * n + i), add_edge(2 * n + i, n + i);
	int l = 1, r = 1;
	for(int i = 1; i ^ (n << 1) + 1; i++)
	{
		while(l < i && a[i].x - a[l].x >= x) l++;
		while(r < (n << 1) && a[r + 1].x - a[i].x < x) r++;
		if(l < i) Seg::link(1, l, i - 1, a[i].id);
		if(r > i) Seg::link(1, i + 1, r, a[i].id);
	}
	for(int i = 1; i ^ (n << 1) + 1; i++) if(!dfn[i]) dfs(i);
	for(int i = 1; i ^ (n << 1) + 1; i++) if(num[i] == num[i + (n << 1)]) return 0;
	return 1;
}
/*----------------------------------------------------------*/
int main() {
	n = read(); int l = 0, r = 1e9;
	for(int i = 1; i ^ n + 1; i++) a[i] = (Node){read(), i}, a[i + n] = (Node){read(), i + n};
	std::sort(a + 1, a + 1 + (n << 1), cmp);
	while(l <= r)
	{
		int mid = l + r >> 1;
		if(check(mid)) l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	Print(ans);
	return 0;
}
/*
存点的坐标及点标号 按照坐标排序 
一倍为 x 
二倍为 y
三倍为 反x
四倍为 反y 
两倍 n 建树
2-set 自建边
点向区间建边
tarjan判是否合法 
*/

P5787 二分图 /【模板】线段树分治

对区间建线段树 每条边出现的都是一个区间 扩展值域并查集维护是否为二分图 按秩合并 支持撤销操作 最后遍历一遍统计答案

/*
  Source: P5787 二分图 /【模板】线段树分治
*/
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pr std::pair <int, int> 
#define mk std::make_pair
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
/*----------------------------------------------------------*/
int n, m, k, top, fa[B << 1], dep[B << 1];
pr st[B << 1];
struct edge {int u, v;} a[B << 1];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
int find(int x) {return x == fa[x] ? x : find(fa[x]);}
void merge(int x, int y) {
	if(x == y) return ; if(dep[x] > dep[y]) Swap(x, y); fa[x] = y;
	st[++top] = mk(x, dep[x] == dep[y]); dep[y] += dep[x] == dep[y];
}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r; std::vector <int> kt;} t[B << 2];
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) return ;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
	}
	void up_date(int p, int l, int r, int f) {
		if(l <= t[p].l && t[p].r <= r) {t[p].kt.push_back(f); return ;}
		if(l <= mid) up_date(ls(p), l, r, f); if(r > mid) up_date(rs(p), l, r, f);
	}
	void query(int p) {
		bool flag = 1; int cnt = top;
		for(int i = 0, ed = t[p].kt.size(); i ^ ed; i++)
		{
			int x = t[p].kt[i], u = find(a[x].u), v = find(a[x].v);
			if(u == v) {for(int j = t[p].l; j ^ t[p].r + 1; j++) puts("No"); flag = 0; break;}
			merge(find(a[x].u + n), v); merge(u, find(a[x].v + n));
		}
		if(flag) if(t[p].l == t[p].r) puts("Yes"); else query(ls(p)), query(rs(p));
		while(top > cnt) dep[fa[st[top].first]] -= st[top].second, fa[st[top].first] = st[top].first, top--;
	}
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read(); k = read(); Seg::build(1, 1, k);
	for(int i = 1; i ^ m + 1; i++)
	{
		a[i] = (edge){read(), read()};
		int x = read(), y = read();
		Seg::up_date(1, x + 1, y, i);
	}
	for(int i = 1; i ^ n + 1; i++) fa[i] = i, fa[i + n] = i + n;
	Seg::query(1);
	return 0;
}

P5227 连通图

将删边转化为加边 \(c\) 很小 维护每条边存在的时间 线段树分治

注意并查集要按秩合并 按子树大小合并是过不了的

/*
  Source: P5227 [AHOI2013]连通图
*/
#include<vector>
#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
/*----------------------------------------------------------*/
int n, m, K, top, fa[B << 1], siz[B << 1], dep[B << 1];
struct Node {int x, y, z;} st[B << 1]; 
struct edge {int u, v;} a[B << 1];
std::vector <int> ti[B << 1];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
int find(int x) {return fa[x] == x ? x : find(fa[x]);}
int merge(int x, int y) {
	if(dep[x] < dep[y]) Swap(x, y); dep[x] += dep[x] == dep[y]; siz[x] += siz[y];
	fa[y] = x; st[++top] = (Node){y, siz[y], dep[x] == dep[y]}; return x;
}
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define mid (t[p].l + t[p].r >> 1)
	struct node {int l, r; std::vector <int> k;} t[B << 3];
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) return ;
		build(ls(p), l, mid); build(rs(p), mid + 1, r);
	}
	void up_date(int p, int l, int r, int f) {
		if(l <= t[p].l && t[p].r <= r) {t[p].k.push_back(f); return ;}
		if(l <= mid) up_date(ls(p), l, r, f); if(r > mid) up_date(rs(p), l, r, f);
	}
	void query(int p) {
		bool flag = 0; int cnt = top;
		for(int i = 0, ed = t[p].k.size(); i ^ ed; i++)
		{
			int x = t[p].k[i], u = find(a[x].u), v = find(a[x].v);
			if(u == v) continue; int y = merge(u, v);
			if(siz[y] == n) {for(int j = t[p].l; j ^ t[p].r + 1; j++) puts("Connected"); flag = 1; break;}
		}
		if(!flag) if(t[p].l == t[p].r) puts("Disconnected"); else query(ls(p)), query(rs(p));
		while(top > cnt) siz[fa[st[top].x]] -= st[top].y, dep[fa[st[top].x]] -= st[top].z, fa[st[top].x] = st[top].x, top--;
	}
}
/*----------------------------------------------------------*/
int main() {
	n = read(); m = read();
	for(int i = 1; i ^ m + 1; i++) a[i] = (edge){read(), read()};
	K = read(); Seg::build(1, 1, K);
	for(int i = 1; i ^ K + 1; i++)
	{
		int x = read();
		for(int j = 0; j ^ x; j++) ti[read()].push_back(i);
	}
	for(int i = 1; i ^ m + 1; i++)
	{
		int ed = ti[i].size();
		if(!ed) {Seg::up_date(1, 1, K, i); continue;}
		if(ti[i][0] != 1) Seg::up_date(1, 1, ti[i][0] - 1, i);
		if(ti[i][ed - 1] != K) Seg::up_date(1, ti[i][ed - 1] + 1, K, i);
		if(ed == 1) continue;
		for(int j = 1; j ^ ed; j++) if(ti[i][j] != ti[i][j - 1] + 1) Seg::up_date(1, ti[i][j - 1] + 1, ti[i][j] - 1, i);
	}
	for(int i = 1; i ^ n + 1; i++) siz[i] = 1, fa[i] = i;
	Seg::query(1);
	return 0;
}
posted @ 2021-07-20 20:42  Blank_space  阅读(51)  评论(0编辑  收藏  举报
// // // // // // //