@codeforces - [email protected] Little Artem and 2-SAT

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定两个 2-sat 问题,询问两个问题的解集是否相同。

如果不相同,构造一组解 {xi},使得这个解是其中一个问题的解同时不是另一个问题的解。

原题链接。

@[email protected]

如果两者解集都为空,那么解集相同。
如果两者只有一个解集为空,则取另一个问题的任意解即可。

如果都有解,先跑个 bitset 优化的传递闭包,方便接下来的处理。

此时有一些变量的值是确定的(即可以从该变量的 0/1 状态到达该变量的 1/0 状态)。
如果有一个变量在问题 A 中确定而在问题 B 中不确定,则强制令 B 中该变量的值为 A 中值取反,暴力求此时 B 中的任意解。
如果有一个变量在两个问题中都确定但是确定的值不同,直接取问题 A 的任意解即可。

现在确定的变量都长一样了。但是不确定的变量可能相互制约,所以还是不能断定两个问题解集相同。

如果两个不确定的变量对应的结点 u, v,在问题 A 中有边相连 u -> v,在问题 B 中没有边相连,我们就可以构造出一组解。
在问题 B 中强制令 u 为真,v 为假,构造出 B 的任意解,该解在 A 中一定不成立。

@accepted [email protected]

#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;

const int MAXN = 2000;

bitset<MAXN>G[2][MAXN]; int n;

int abs(int x) {return x >= 0 ? x : -x;}

bool f[2]; int a[2][MAXN + 5];
void get(int o) {
    for(int i=0;i<n;i++) {
        if( a[o][i] == -1 ) {
            for(int j=0;j<n;j++)
                if( (a[o][j] == 0 && G[o][i<<1|1][j<<1|1]) || (a[o][j] == 1 && G[o][i<<1|1][j<<1]) ) {
                    a[o][i] = 0;
                    break;
                }
            if( a[o][i] == -1 ) a[o][i] = 1;
        }
        printf("%d ", a[o][i]);
    }
    exit(0);
}

int main() {
    int m[2]; scanf("%d%d%d", &n, &m[0], &m[1]);
    for(int o=0;o<2;o++) {
        for(int i=1;i<=m[o];i++) {
            int a, b; scanf("%d%d", &a, &b);
            int p = (a < 0), q = (b < 0); a = abs(a) - 1, b = abs(b) - 1;
            G[o][a<<1|p][b<<1|(!q)] = true, G[o][b<<1|q][a<<1|(!p)] = true;
        }
        for(int i=0;i<(n<<1);i++) G[o][i][i] = true;
        for(int k=0;k<(n<<1);k++)
            for(int i=0;i<(n<<1);i++)
                if( G[o][i][k] ) G[o][i] |= G[o][k];
        for(int i=0;i<n;i++) {
            if( G[o][i<<1][i<<1|1] && G[o][i<<1|1][i<<1] ) {
                f[o] = true;
                break;
            }
            else if( G[o][i<<1][i<<1|1] ) a[o][i] = 1;
            else if( G[o][i<<1|1][i<<1] ) a[o][i] = 0;
            else a[o][i] = -1;
        }
    }
    if( f[0] && f[1] ) puts("SIMILAR");
    else if( f[0] ) get(1);
    else if( f[1] ) get(0);
    else {
        for(int i=0;i<n;i++)
            if( a[0][i] != a[1][i] ) {
                if( a[0][i] == -1 ) a[0][i] = (!a[1][i]), get(0);
                else if( a[1][i] == -1 ) a[1][i] = (!a[0][i]), get(1);
                else get(0);
                return 0;
            }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++) {
                if( a[0][i] == -1 && a[0][j] == -1 ) {
                    if( G[0][i<<1|1][j<<1|1] != G[1][i<<1|1][j<<1|1] ) {
                        if( G[0][i<<1|1][j<<1|1] == 0 )
                            a[0][i] = 1, a[0][j] = 0, get(0);
                        else a[1][i] = 1, a[1][j] = 0, get(1);
                        return 0;
                    }
                    else if( G[0][i<<1|1][j<<1] != G[1][i<<1|1][j<<1] ) {
                        if( G[0][i<<1|1][j<<1] == 0 )
                            a[0][i] = 1, a[0][j] = 1, get(0);
                        else a[1][i] = 1, a[1][j] = 1, get(1);
                        return 0;
                    }
                    else if( G[0][i<<1][j<<1|1] != G[1][i<<1][j<<1|1] ) {
                        if( G[0][i<<1][j<<1|1] == 0 )
                            a[0][i] = 0, a[0][j] = 0, get(0);
                        else a[1][i] = 0, a[1][j] = 0, get(1);
                        return 0;
                    }
                    else if( G[0][i<<1][j<<1] != G[1][i<<1][j<<1] ) {
                        if( G[0][i<<1][j<<1] == 0 )
                            a[0][i] = 0, a[0][j] = 1, get(0);
                        else a[1][i] = 0, a[1][j] = 1, get(1);
                        return 0;
                    }
                }
            }
        puts("SIMILAR");
    }
}

@[email protected]

注意有一些边界情况,预先给每个结点连个自环。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12244194.html

时间: 2024-08-29 10:18:25

@codeforces - [email protected] Little Artem and 2-SAT的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m