题目链接

https://codeforces.com/problemset/problem/1007/D

题解

这道题本身并不难,这里只是记录一下 2-SAT 的前缀优化建图的相关内容。

由于问题的本质是给定许多二元集合,判断是否能从每一个二元集合中选出一个元素,使得所有选出的元素合法,因此考虑使用 2-SAT 解决该问题。

不难发现,使用 2-SAT 解决该问题的复杂度瓶颈在于建图。

我们为每一种颜色 \(i\) 对应的两条路径赋上编号。首先,我们需要为每一条树边记录包含该条边的所有路径的编号。可以将原树树链剖分之后,按结点的 dfs 序建出线段树,将路径的编号添加到线段树对应的结点上。这样,包含一条树边的所有路径编号储存在该边在线段树上对应的叶子结点以及该叶子结点的各级祖先上。因此,我们只需要通过建图来保证线段树的每一个叶子结点及其各级祖先包含的所有编号中,最多只能选择一个编号即可。

考虑使用前缀优化建图。

简单来说,前缀优化建图常用来处理某个命题集合中最多有一个命题成立或不成立的情况(不失一般性地,接下来只分析最多有一个命题成立的情况,在本题中,命题成立即为选择对应编号)。假设这些命题的编号为 \(1 \sim x\),那么我们新建 \(x\) 个结点,用这些结点来表示集合的某个前缀的所有命题是否均不成立,即:这些结点中,第 \(i\) 个结点若为真,则命题 \(1 \sim i\) 均不成立,否则命题 \(1 \sim i\) 中存在一个命题成立。

定义一次建边 \((u \rightarrow v)\) 为:建 \((u\) 为真 \(\rightarrow\) \(v\) 为真\()\) 与 \((v\) 为假 \(\rightarrow u\) 为假\()\) 两条对称边。那么我们只需要建如下三类边即可:

  • \(1 \sim i\) 的所有命题均不成立 \(\rightarrow\) \(1 \sim i - 1\) 的所有命题均不成立
  • 命题 \(i\) 成立 \(\rightarrow\) \(1 \sim i\) 的所有命题存在成立
  • 命题 \(i\) 成立 \(\rightarrow\) \(1 \sim i - 1\) 的所有命题均不成立

本题的建图略有不同,由于在线段树上,所有的前缀本身就构成了一个树形结构,因此相同的前缀可以共用结点。不难发现,最后建出的总结点数(及边数)和线段树上存储的编号总数同阶,为 \(O(m \log^2 n)\)。由于初始在线段树上添加标记的时间复杂度也为 \(O(m \log^2 n)\),因此总时间复杂度为 \(O(m \log^2 n)\)。

代码

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, maxnode = N * 60;

void cmin(int& x, int y) {
if (x > y) {
x = y;
}
} int n, m, pa[N], dep[N], size[N], heavy[N], dfn[maxnode], dfn_cnt, top[N], node_cnt, low[maxnode], sccno[maxnode], scc;
vector<int> graph[N], graph_t[maxnode], nodes[N << 2], stack_t; void dfs1(int u, int pa) {
size[u] = 1;
for (auto v : graph[u]) {
if (v != pa) {
::pa[v] = u;
dep[v] = dep[u] + 1;
dfs1(v, u);
size[u] += size[v];
if (size[v] > size[heavy[u]]) {
heavy[u] = v;
}
}
}
} void dfs2(int u, int t) {
top[u] = t;
dfn[u] = ++dfn_cnt;
if (heavy[u]) {
dfs2(heavy[u], t);
for (auto v : graph[u]) {
if (v != pa[u] && v != heavy[u]) {
dfs2(v, v);
}
}
}
} void add_edge(int u, int v) {
graph_t[u].push_back(v);
graph_t[v ^ 1].push_back(u ^ 1);
} #define lo (o<<1)
#define ro (o<<1|1) void modify(int l, int r, int o, int ql, int qr, int id) {
if (ql <= l && r <= qr) {
nodes[o].push_back(id);
} else {
int mid = l + r >> 1;
if (ql <= mid) {
modify(l, mid, lo, ql, qr, id);
} if (qr > mid) {
modify(mid + 1, r, ro, ql, qr, id);
}
}
} void build(int l, int r, int o, int lastid) {
int ql = ++node_cnt;
int qr = (node_cnt += nodes[o].size());
if (qr > ql) {
add_edge(qr << 1 | 1, qr - 1 << 1 | 1);
} else if (lastid) {
add_edge(ql << 1 | 1, lastid << 1 | 1);
}
for (int i = 0; i < nodes[o].size(); ++i) {
int id = nodes[o][i];
if (i > 0) {
add_edge(ql + i << 1 | 1, ql + i - 1 << 1 | 1);
} else if (lastid) {
add_edge(ql << 1 | 1, lastid << 1 | 1);
}
add_edge(id, ql + i << 1);
if (i > 0) {
add_edge(ql + i - 1 << 1, id ^ 1);
} else if (lastid) {
add_edge(lastid << 1, id ^ 1);
}
}
if (l < r) {
int mid = l + r >> 1;
build(l, mid, lo, qr);
build(mid + 1, r, ro, qr);
}
} void add_tag(int u, int v, int id) {
for (; top[u] != top[v]; u = pa[top[u]]) {
if (dep[top[u]] < dep[top[v]]) {
swap(u, v);
}
modify(2, n, 1, dfn[top[u]], dfn[u], id);
}
if (dep[u] > dep[v]) {
swap(u, v);
}
if (dfn[u] < dfn[v]) {
modify(2, n, 1, dfn[u] + 1, dfn[v], id);
}
} void tarjan(int u) {
stack_t.push_back(u);
dfn[u] = low[u] = ++dfn_cnt;
for (auto v : graph_t[u]) {
if (!dfn[v]) {
tarjan(v);
cmin(low[u], low[v]);
} else if (!sccno[v]) {
cmin(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
++scc;
while (1) {
int x = stack_t.back();
stack_t.pop_back();
sccno[x] = scc;
if (x == u) {
break;
}
}
}
} int main() {
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
graph[u].push_back(v);
graph[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 1);
scanf("%d", &m);
node_cnt = m;
for (int i = 1; i <= m; ++i) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
add_tag(a, b, i << 1);
add_tag(c, d, i << 1 | 1);
}
build(2, n, 1, 0);
memset(dfn, 0, sizeof dfn);
dfn_cnt = 0;
for (int i = 2; i <= (node_cnt << 1 | 1); ++i) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= m; ++i) {
if (sccno[i << 1] == sccno[i << 1 | 1]) {
return puts("NO"), 0;
}
}
puts("YES");
for (int i = 1; i <= m; ++i) {
printf("%d\n", sccno[i << 1] < sccno[i << 1 | 1] ? 1 : 2);
}
return 0;
}

CF1007D. Ants(树链剖分+线段树+2-SAT及前缀优化建图)的更多相关文章

  1. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  3. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  4. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  5. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  6. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  7. Aizu 2450 Do use segment tree 树链剖分+线段树

    Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...

  8. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

  9. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

  10. bzoj2243[SDOI2011]染色 树链剖分+线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 9012  Solved: 3375[Submit][Status ...

随机推荐

  1. 74款安卓和IOS app源码地址

    知乎专栏App https://github.com/bxbxbai/ZhuanLan WeChat高仿微信 项目地址: https://github.com/motianhuo/wechat Gan ...

  2. Some details of UIKit

    [Some details of UIKit] 1.UIViewController的toolbarItems属性与UINavigationController配合使用. 2.The view for ...

  3. web应用安全权威指南(文摘)

    第1章 什么是web应用的安全隐患 第3章 Web安全基础,HTTP,会话管理,同源策略 content_length 字节数 content_type mime类型 百分号编码 referer :请 ...

  4. B-spline Curves 学习之B样条曲线的移动控制点、修改节点分析(7)

    B-spline Curves: Moving Control Points 本博客转自前人的博客的翻译版本,前几章节是原来博主的翻译内容,但是后续章节博主不在提供翻译,后续章节我在完成相关的翻译学习 ...

  5. Oracle EBS R12多组织访问架构

    关于R12的新特性Multi-Org Access Control(MOAC).Oracle宣传的好处主要有:1.enable users to access to secured data in o ...

  6. SQL 判断数据库是否有相关表 字段

    --判断数据库是否有相关表 if exists (select 1 from sysobjects where id = object_id(' 表名 ') and type = ' U ' ); - ...

  7. unity 移动物体的方式

    1. 简介 在Unity3D中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position. 2. 通过Transform组件移动物体 Transform 组件用于描述物体在 ...

  8. cannot connect cube with sharepoint dashboard designer

    需要下载WindowsIdentityFoundation-SDK-4.0进行安装

  9. python--内置常用模块(一) collections 队列 time模块 functiontools

    一. 模块的简单认识 模块就是我们把装有特定功能的代码进行归类的结果.从代码编写的单位来看我们的程序,从小到大的顺序:一条代码 < 语句块 < 代码块(函数,类) < 模块,我们目前 ...

  10. DP刷题

    http://blog.csdn.net/a1dark/article/details/17115137