作者: KSkun

点分治原理与实现

点分治原理与实现

树分治系列: 点分治原理与实现 | KSkun’s Blog 边分治原理及实现 

[BZOJ3589]动态树 题解

[BZOJ3589]动态树 题解

题目地址:BZOJ:Problem 3589. — 动态树 题目描述 小明在楼 

[ZJOI2011]道馆之战 题解

[ZJOI2011]道馆之战 题解

题目地址:洛谷:【P4679】[ZJOI2011]道馆之战 – 洛谷、BZOJ:Problem 2325. — [ZJOI2011]道馆之战

题目描述

馆主为了加大难度,将道馆分成了n个房间,每个房间中是两个冰块或障碍,表示一列冰地。任意两个房间之间均有且仅有一条路径相连,即这n个房间构成一个树状结构。每个房间分成了A和B两个区域,每一区域都是一个薄冰块或者障碍物。每次只能移动到相邻房间的同一类区域(即若你现在在这个房间的A区域,那么你只能移动到相邻房间的A区域)或这个房间的另一区域。现在挑战者从房间u出发,馆主在房间v,那么挑战者只能朝接近馆主所在房间的方向过去。一开始挑战者可以在房间u的任意一个冰块区域内。如果挑战者踩过的冰块数达到了最大值(即没有一种方案踩过的冰块数更多了),那么当挑战者走到最后一个冰块上时,他会被瞬间传送到馆主面前与馆主进行道馆战。自从馆主修改规则后已经经过了m天,每天要么是有一个挑战者来进行挑战,要么就是馆主将某个房间进行了修改。对于每个来的挑战者,你需要计算出他若要和馆主进行战斗需要经过的冰块数。

输入输出格式

输入格式:
第一行包含两个正整数n和m。第2行到第n行,每行包含两个正整数x和y,表示一条连接房间x和房间y的边。房间编
号为1…n。接下来n行,每行包含两个字符。第n + k行表示房间k的两个区域,第一个字符为A区域,第二个字符为
B区域。其中“.”(ASCII码为46)表示是薄冰块,“#”(ASCII码为35)表示是障碍物。最后的m行,每行一个操作:
C u s:将房间u里的两个区域修改为s。
Q u v:询问挑战者在房间u,馆主在房间v时,挑战者能与馆主进行挑战需要踩的冰块数。如果房间u的两个区域
都是障碍物,那么输出0。
N≤ 30 000
M ≤ 80 000

输出格式:
包含若干行,每行一个整数。即对于输入中的每个询问,依次输出一个答案。

输入输出样例

输入样例#1:

5 3
1 2
2 3
2 4
1 5
.#
..
#.
.#
..
Q 5 3
C 1 ##
Q 4 5

输出样例#1:

6
3

题解

链上信息用树剖总是没错的,套个线段树。接下来的内容只研究区间信息和合并。
首先我们要求一个点出发能走的最远距离,自然要把这个答案记起来,那么用far[2][2]表示区间左1左2右1右2开始走最远距离(这里其实有点像[SHOI2008]堵塞的交通 题解 | KSkun’s Blog)。为了区间合并,我们还要记下dis[2][2]表示区间左边两个区域到右边两个区域分别的最远路径长度。
初始的时候,如果不能走,可以以-INF为初值,这样后面取max比较好办。以长度为1的区间开始设置初始值,设置的部分比较容易理解可以看代码或者类比SHOI毒瘤题。合并的时候,dis数组计算应当枚举跨越mid时是从1区域还是2区域通行的,far数组应当对跨越mid和不跨越mid分别统计。这个图示与SHOI毒瘤题其实长得差不多,可以参考那个题,结合代码理解。
查询的时候,可能要翻转其中的一半答案再合并成最后的答案。swap的时候可以考虑记下来起点在当前的哪一端。
题解参考了【BZOJ-2325】道馆之战 树链剖分 + 线段树 – DaD3zZ – 博客园,感谢原作者。

代码

// Code by KSkun, 2018/3
#include <cstdio>

#include <vector>
#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

inline bool isstatus(char c) {
    return c == '.' || c == '#';
}

inline bool readstatus() {
    char c;
    while(!isstatus(c = fgc()));
    return c == '.';
}

inline bool isop(char c) {
    return c == 'Q' || c == 'C';
}

inline char readop() {
    char c;
    while(!isop(c = fgc()));
    return c;
}

const int MAXN = 300005, INF = 1e9;

int n, m, ut, vt;
char op;
bool w[MAXN][2];
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];

inline void addedge(int u, int v) {
    gra[u].push_back(v);
    gra[v].push_back(u);
}

inline void dfs1(int u) {
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v] + 1;
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

inline void dfs2(int u, int tp) {
    top[u] = tp;
    dfn[u] = ++cnt;
    ptn[dfn[u]] = u;
    if(son[u]) dfs2(son[u], tp);
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

struct Node {
    int dis[2][2], far[2][2];
} sgt[MAXN << 2];

inline void merge(Node &rt, Node ls, Node rs) {
    rt.dis[0][0] = std::max(std::max(ls.dis[0][0] + rs.dis[0][0], ls.dis[0][1] + rs.dis[1][0]), -INF);
    rt.dis[0][1] = std::max(std::max(ls.dis[0][0] + rs.dis[0][1], ls.dis[0][1] + rs.dis[1][1]), -INF);
    rt.dis[1][0] = std::max(std::max(ls.dis[1][0] + rs.dis[0][0], ls.dis[1][1] + rs.dis[1][0]), -INF);
    rt.dis[1][1] = std::max(std::max(ls.dis[1][0] + rs.dis[0][1], ls.dis[1][1] + rs.dis[1][1]), -INF);
    rt.far[0][0] = std::max(ls.far[0][0], std::max(ls.dis[0][0] + rs.far[0][0], ls.dis[0][1] + rs.far[0][1]));
    rt.far[0][1] = std::max(ls.far[0][1], std::max(ls.dis[1][0] + rs.far[0][0], ls.dis[1][1] + rs.far[0][1]));
    rt.far[1][0] = std::max(rs.far[1][0], std::max(rs.dis[0][0] + ls.far[1][0], rs.dis[1][0] + ls.far[1][1]));
    rt.far[1][1] = std::max(rs.far[1][1], std::max(rs.dis[0][1] + ls.far[1][0], rs.dis[1][1] + ls.far[1][1]));
}

inline void reverse(Node &x) {
    std::swap(x.dis[0][1], x.dis[1][0]);
    std::swap(x.far[0][0], x.far[1][0]);
    std::swap(x.far[0][1], x.far[1][1]);
}

inline void build(int o, int l, int r) {
    if(l == r) {
        sgt[o].dis[0][0] = w[ptn[l]][0] ? 1 : -INF;
        sgt[o].far[0][0] = sgt[o].far[1][0] = w[ptn[l]][0];
        sgt[o].dis[1][1] = w[ptn[l]][1] ? 1 : -INF;
        sgt[o].far[0][1] = sgt[o].far[1][1] = w[ptn[l]][1];
        if(w[ptn[l]][0] && w[ptn[l]][1]) {
            sgt[o].far[0][0] = sgt[o].far[0][1] = sgt[o].far[1][0] = sgt[o].far[1][1] = 
                sgt[o].dis[0][1] = sgt[o].dis[1][0] = 2;
        } else {
            sgt[o].dis[0][1] = sgt[o].dis[1][0] = -INF;
        }
        return;
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    build(lch, l, mid);
    build(rch, mid + 1, r);
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline void modify(int o, int l, int r, int x) {
    if(l == r) {
        sgt[o].dis[0][0] = w[ptn[l]][0] ? 1 : -INF;
        sgt[o].far[0][0] = sgt[o].far[1][0] = w[ptn[l]][0];
        sgt[o].dis[1][1] = w[ptn[l]][1] ? 1 : -INF;
        sgt[o].far[0][1] = sgt[o].far[1][1] = w[ptn[l]][1];
        if(w[ptn[l]][0] && w[ptn[l]][1]) {
            sgt[o].far[0][0] = sgt[o].far[0][1] = sgt[o].far[1][0] = sgt[o].far[1][1] = 
                sgt[o].dis[0][1] = sgt[o].dis[1][0] = 2;
        } else {
            sgt[o].dis[0][1] = sgt[o].dis[1][0] = -INF;
        }
        return;
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(x <= mid) modify(lch, l, mid, x);
    else modify(rch, mid + 1, r, x);
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline Node query(int o, int l, int r, int ll, int rr) {
    if(l == ll && r == rr) {
        return sgt[o];
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(rr <= mid) {
        return query(lch, l, mid, ll, rr);
    } else if(ll > mid) {
        return query(rch, mid + 1, r, ll, rr);
    } else {
        Node ls = query(lch, l, mid, ll, mid), rs = query(rch, mid + 1, r, mid + 1, rr), res;
        merge(res, ls, rs);
        return res;
    }
}

inline int query(int u, int v) {
    Node su, sv, t, t1;
    bool fu = false, fv = false, iu = true;
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(tu, tv);
            std::swap(u, v);
            std::swap(su, sv);
            std::swap(fu, fv);
            iu ^= 1;
        }
        t = query(1, 1, n, dfn[tv], dfn[v]);
        if(fv) merge(t1, t, sv), sv = t1; else sv = t, fv = true;
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) {
        std::swap(u, v);
        std::swap(su, sv);
        std::swap(fu, fv);
        iu ^= 1;
    }
    t = query(1, 1, n, dfn[u], dfn[v]);
    if(fv) merge(t1, t, sv), sv = t1; else sv = t, fv = true;
    if(fu) reverse(su), merge(t1, su, sv); else t1 = sv;
    return std::max(t1.far[!iu][0], t1.far[!iu][1]);
}

int main() {
    n = readint(); m = readint();
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        addedge(ut, vt);
    }
    for(int i = 1; i <= n; i++) {
        w[i][0] = readstatus();
        w[i][1] = readstatus();
    }
    dfs1(1);
    dfs2(1, 1);
    build(1, 1, n);
    while(m--) {
        op = readop();
        if(op == 'Q') {
            ut = readint(); vt = readint();
            printf("%d\n", query(ut, vt));
        }
        if(op == 'C') {
            ut = readint(); w[ut][0] = readstatus(); w[ut][1] = readstatus();
            modify(1, 1, n, dfn[ut]);
        }
    }
    return 0;
}
[JLOI2011]不重复数字 题解

[JLOI2011]不重复数字 题解

题目地址:洛谷:【P4305】[JLOI2011]不重复数字 – 洛谷、BZO 

[SDOI2011]染色 题解

[SDOI2011]染色 题解

题目地址:洛谷:【P2486】[SDOI2011]染色 – 洛谷、BZOJ:P 

[HAOI2015]树上操作 题解

[HAOI2015]树上操作 题解

题目地址:洛谷:【P3178】[HAOI2015]树上操作 – 洛谷、BZOJ:Problem 4034. — [HAOI2015]树上操作

题目描述

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

输入输出格式

输入格式:
第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

输出格式:
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例

输入样例#1:

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

输出样例#1:

6
9
13

说明

对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。

题解

树剖那篇文章的例题还裸。

代码

 // Code by KSkun, 2018/3
#include <cstdio>

#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 100005;

int n, m, w[MAXN], ut, vt, op, xt, at;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];

inline void addedge(int u, int v) {
    gra[u].push_back(v);
    gra[v].push_back(u);
}

inline void dfs1(int u) {
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v] + 1;
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

inline void dfs2(int u, int tp) {
    top[u] = tp;
    dfn[u] = ++cnt;
    ptn[dfn[u]] = u;
    if(son[u]) dfs2(son[u], tp);
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

struct Node {
    LL val, add;
} sgt[MAXN << 2];

inline void pushdown(int o, int l, int r) {
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(sgt[o].add) {
        sgt[lch].add += sgt[o].add;
        sgt[rch].add += sgt[o].add;
        sgt[lch].val += sgt[o].add * (mid - l + 1);
        sgt[rch].val += sgt[o].add * (r - mid);
        sgt[o].add = 0;
    }
}

inline void build(int o, int l, int r) {
    if(l == r) {
        sgt[o].val = w[ptn[l]];
        return;
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    build(lch, l, mid);
    build(rch, mid + 1, r);
    sgt[o].val = sgt[lch].val + sgt[rch].val;
}

inline void add(int o, int l, int r, int ll, int rr, LL v) {
    if(l >= ll && r <= rr) {
        sgt[o].add += v;
        sgt[o].val += v * (r - l + 1);
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(ll <= mid) add(lch, l, mid, ll, rr, v);
    if(rr > mid) add(rch, mid + 1, r, ll, rr, v);
    sgt[o].val = sgt[lch].val + sgt[rch].val;
}

inline LL query(int o, int l, int r, int ll, int rr) {
    if(l >= ll && r <= rr) {
        return sgt[o].val;
    }
    pushdown(o, l, r);
    LL res = 0;
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(ll <= mid) res += query(lch, l, mid, ll, rr);
    if(rr > mid) res += query(rch, mid + 1, r, ll, rr);
    return res;
}

inline void addpoint(int x, LL v) {
    add(1, 1, n, dfn[x], dfn[x], v);
}

inline void addsubtree(int x, LL v) {
    add(1, 1, n, dfn[x], dfn[x] + siz[x], v);
}

inline LL querychain(int x) {
    int tp = top[x];
    LL res = 0;
    while(x) {
        res += query(1, 1, n, dfn[tp], dfn[x]);
        x = fa[tp];
        tp = top[x];
    }
    return res;
}

int main() {
    n = readint(); m = readint();
    for(int i = 1; i <= n; i++) w[i] = readint();
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        addedge(ut, vt);
    }
    dfs1(1);
    dfs2(1, 1);
    build(1, 1, n);
    while(m--) {
        op = readint();
        xt = readint();
        if(op != 3) at = readint();
        if(op == 1) {
            addpoint(xt, at);
        }
        if(op == 2) {
            addsubtree(xt, at);
        }
        if(op == 3) {
            printf("%lld\n", querychain(xt));
        }
    }
    return 0;
}
树链剖分(轻重链剖分)原理与实现

树链剖分(轻重链剖分)原理与实现

概述 树链剖分是一种将树上的链划分为轻重链,从而实现降低复杂度的处理方式。剖分后的树,单次 

[HDU5385]The path 题解

[HDU5385]The path 题解

题目地址:HDUOJ:Problem – 5385 题目描述 You have 

[POJ3683]Priest John’s Busiest Day 题解 & 2-SAT算法的优化

[POJ3683]Priest John’s Busiest Day 题解 & 2-SAT算法的优化

题目地址:POJ:3683 — Priest John’s Busiest Day

题目描述

John is the only priest in his town. September 1st is the John’s busiest day in a year because there is an old legend in the town that the couple who get married on that day will be forever blessed by the God of Love. This year N couples plan to get married on the blessed day. The i-th couple plan to hold their wedding from time Si to time Ti. According to the traditions in the town, there must be a special ceremony on which the couple stand before the priest and accept blessings. The i-th couple need Di minutes to finish this ceremony. Moreover, this ceremony must be either at the beginning or the ending of the wedding (i.e. it must be either from Si to Si + Di, or from Ti – Di to Ti). Could you tell John how to arrange his schedule so that he can present at every special ceremonies of the weddings.
Note that John can not be present at two weddings simultaneously.
每个婚礼各有两段时间可以选择,要求每对新人的婚礼的时间不能重叠,求可行方案。

输入输出格式

输入格式:
The first line contains a integer N ( 1 ≤ N ≤ 1000).
The next N lines contain the Si, Ti and Di. Si and Ti are in the format of hh:mm.

输出格式:
The first line of output contains “YES” or “NO” indicating whether John can be present at every special ceremony. If it is “YES”, output another N lines describing the staring time and finishing time of all the ceremonies.

输入输出样例

输入样例#1:

2
08:00 09:00 30
08:15 09:00 20

输出样例#1:

YES
08:00 08:30
08:40 09:00

题解

本题同样是比较裸的2-SAT题目,只是利用本题来讲解2-SAT算法的优化如何实现。下面的内容也参考了《2-SAT算法浅析》(华师一附中 赵爽)的内容。
首先是建图的部分,我们对于任意一对可能冲突的时间段分别建边即可。剩下的事情就是跑一遍2-SAT。
我们知道,2-SAT图中的有向边表示的是一种“必须选择”的关系,那么如果图中有一个强连通分量,这个SCC中只要选中了就比如选里面所有的点。那么原来看上去很大的图就可以直接缩点成一个比较小的图,我们再考虑在小图上的工作。注意,缩点后的图中,SCC间的边要反向,原因将会在之后提到。
首先,如果有对立点存在于同一个SCC中,那么无解。
接着我们考虑利用这个有向无环图的拓扑序获得答案。我们先选中一个拓扑序列中未标记的点,然后DFS处理这个点的对立点所在的SCC。既然对立点所在SCC不能被选中,那么要求必须选中它的点就也不会选中,所以在原图的边的反向上,DFS应当沿着入边一路处理到入度为0的点,这里建反边的优势就体现出来了。重复这一工作,直到所有点都有一个标记“选中”或“不选”。
接着按照所在SCC的标记来输出可行解即可。

代码

以下代码借鉴了「POJ3683」Priest John’s Busiest Day – 2-SAT – hzwer.com的写法,感谢hzwer学长。

// Code by KSkun, 2018/3
#include <cstdio>

#include <vector>
#include <stack>
#include <queue>
#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 2005;

std::vector<int> gra[MAXN], gra2[MAXN];
int n, begin[MAXN], end[MAXN], mark[MAXN], deg[MAXN];
int dfn[MAXN], low[MAXN], tot, scc, belong[MAXN], op[MAXN];
std::stack<int> sta;
std::queue<int> que;
bool insta[MAXN];

inline bool notconflict(int x, int y) {
    return begin[x] >= end[y] || begin[y] >= end[x];
}

inline void tarjan(int u) {
    dfn[u] = low[u] = ++tot;
    sta.push(u); insta[u] = true;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = std::min(low[u], low[v]);
        } else if(insta[v]) {
            low[u] = std::min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {
        int t = 0; scc++;
        while(t != u) {
            t = sta.top(); sta.pop();
            insta[t] = false;
            belong[t] = scc;
        }
    }
}

inline void dfs(int u) {
    if(mark[u]) return;
    mark[u] = -1;
    for(int i = 0; i < gra2[u].size(); i++) {
        int v = gra2[u][i];
        dfs(v);
    }
}

inline void toposort() {
    for(int i = 1; i <= scc; i++) {
        if(!deg[i]) que.push(i);
    }
    while(!que.empty()) {
        int u = que.front(); que.pop();
        if(mark[u]) continue;
        mark[u] = 1;
        dfs(op[u]);
        for(int i = 0; i < gra2[u].size(); i++) {
            int v = gra2[u][i];
            deg[v]--;
            if(!deg[v]) que.push(v);
        }
    }
}

int main() {
    n = readint();
    for(int i = 1; i <= n; i++) {
        begin[i << 1] = readint() * 60 + readint();
        end[(i << 1) - 1] = readint() * 60 + readint();
        int last = readint();
        end[i << 1] = begin[i << 1] + last;
        begin[(i << 1) - 1] = end[(i << 1) - 1] - last;
    }
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            int a1 = i << 1, a2 = (i << 1) - 1, b1 = j << 1, b2 = (j << 1) - 1;
            if(!notconflict(a1, b1)) {
                gra[a1].push_back(b2);
                gra[b1].push_back(a2);
            }
            if(!notconflict(a1, b2)) {
                gra[a1].push_back(b1);
                gra[b2].push_back(a2);
            }
            if(!notconflict(a2, b1)) {
                gra[a2].push_back(b2);
                gra[b1].push_back(a1);
            }
            if(!notconflict(a2, b2)) {
                gra[a2].push_back(b1);
                gra[b2].push_back(a1);
            }
        }
    }
    for(int i = 1; i <= n << 1; i++) {
        if(!dfn[i]) tarjan(i);
    }
    for(int i = 1; i <= n; i++) {
        if(belong[i << 1] == belong[(i << 1) - 1]) {
            puts("NO");
            return 0;
        } 
        op[belong[i << 1]] = belong[(i << 1) - 1];
        op[belong[(i << 1) - 1]] = belong[i << 1];
    }
    puts("YES");
    for(int u = 1; u <= n << 1; u++) {
        for(int i = 0; i < gra[u].size(); i++) {
            int v = gra[u][i];
            if(belong[u] != belong[v]) {
                gra2[belong[v]].push_back(belong[u]);
                deg[belong[u]]++;
            }
        }
    }
    toposort();
    for(int i = 1; i <= n; i++) {
        if(mark[belong[i << 1]] == 1) {
            printf("%02d:%02d %02d:%02d\n", begin[i << 1] / 60, begin[i << 1] % 60, 
                end[i << 1] / 60, end[i << 1] % 60);
        } else {
            printf("%02d:%02d %02d:%02d\n", begin[(i << 1) - 1] / 60, begin[(i << 1) - 1] % 60, 
                end[(i << 1) - 1] / 60, end[(i << 1) - 1] % 60);
        }
    }
    return 0;
}
[POI2001]Peaceful Commission 题解 & 2-SAT题目解法

[POI2001]Peaceful Commission 题解 & 2-SAT题目解法

题目地址:HDUOJ:Problem – 1814 题目描述 The Publ