奶牛议会 / The Continental Cowngress G

题目链接:ybt金牌导航3-6-3 / luogu P3007

题目大意

有一些人,每个人对众多决案中的两个决案有表示好或不好。
必须要满足每个人两个条件的至少一个。
问你是否有合法解,如果没有判断出来。
有的话你要输出对于每个决案,它是一定要好,还是一定要不要,还是都可以。

思路

首先看着题意都知道它是 2-sat。

建图也很好搞,对于每个人的条件 A,B,那 A 的反向点就连向 B,B 的反向点就连向 A。

但这道题最难的是如何判断每个点的方案是否是一定的。
这里该出两种方法:

每次都 dfs 一次。
什么意思呢,你现在要看 i i i 是否一定要或不要,还是都可能。
那你就分别假设,假设 i i i 是要的,假设 i + n i+n i+n( i i i 反向点)是要的(也就是 i i i 不要)。
然后你知道如果选了一个它后面遍历到的点都要选,那你就 dfs 遍历一遍,然后你就像判断是否有解一样,你判断一下会不会有一个点和它的反向点都被选了,没有就说明可以,否则就是不可以。
那如果两个点都可以,那就是都可能,否则的就是只能选可以的那个。

只 dfs 一次,我们从入度为 0 0 0 的点出发,然后跑 dfs。
如果你跑的时候你跑的路径上同时出现了一个点和它的反向点,那显然肯定是选后遍历到的(也就是你第二次遍历到的,也就是你发现的位置),那它就必选。
然后你知道搞一个标记,把它可以走向的点都也标记成必选就好了。
那如果两个都不是不选,那就是都可以,否则就是那个必选的。

不过要记得,这两个跑 dfs 都是在缩完点的图上进行的。
然后你会发现第二种方法一个点里面有很多个点,它们每个都有对应的点,可能在缩点后点的不同位置。
而且它们只要有一个在路径中这个点就要选,那你就搞个邻接表,把一个点里面点的对应点所在缩点后的位置都记录下来就可以了。

代码

每次 dfs 判断

#include<cstdio>
#include<cstring>
#include<iostream>using namespace std;struct node {int to, nxt;
}e[8001], e_[8001];
int n, m, b, c, ba, ca, tmp, tot, le[2001], KK;
int dfn[2001], low[2001], in[2001], sta[2001];
int le_[2001], KK_;
bool inn[2001];
char cc;void add(int x, int y) {e[++KK] = (node){y, le[x]}; le[x] = KK;
}void tarjan(int now) {dfn[now] = low[now] = ++tmp;sta[++sta[0]] = now;for (int i = le[now]; i; i = e[i].nxt)if (!dfn[e[i].to]) {tarjan(e[i].to);low[now] = min(low[now], low[e[i].to]);}else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);if (dfn[now] == low[now]) {in[now] = ++tot;while (sta[sta[0]] != now) {in[sta[sta[0]]] = tot;sta[0]--;}sta[0]--;}
}void add_(int x, int y) {e_[++KK_] = (node){y, le_[x]}; le_[x] = KK_;
}void dfs(int now) {inn[now] = 1;for (int i = le_[now]; i; i = e_[i].nxt)if (!inn[e_[i].to]) dfs(e_[i].to);
}int check(int x) {memset(inn, 0, sizeof(inn));dfs(in[x]);//直接从这个点开始搜一遍图for (int i = 1; i <= n; i++)//判断选了它会不会有矛盾产生,不会就说明可以选这个点if (inn[in[i]] && inn[in[i + n]]) return 0;return 1;
}int main() {scanf("%d %d", &n, &m);for (int i = 1; i <= m; i++) {scanf("%d", &b);cc = getchar();while (cc != 'Y' && cc != 'N') cc = getchar();if (cc == 'Y') ba = 0;else ba = 1;scanf("%d", &c);cc = getchar();while (cc != 'Y' && cc != 'N') cc = getchar();if (cc == 'Y') ca = 0;else ca = 1;add((ba ^ 1) * n + b, ca * n + c);add((ca ^ 1) * n + c, ba * n + b);}for (int i = 1; i <= n + n; i++)if (!dfn[i]) tarjan(i);for (int i = 1; i <= n; i++)if (in[i] == in[n + i]) {printf("IMPOSSIBLE");return 0;}for (int i = 1; i <= n + n; i++)for (int j = le[i]; j; j = e[j].nxt)if (in[i] != in[e[j].to])add_(in[i], in[e[j].to]);for (int i = 1; i <= n; i++) {int ck1 = check(i), ck2 = check(i + n);if (ck1 && ck2) printf("?");//如果两个都能选就是都行,只有一个就是一定是选这个else if (ck1) printf("Y");else printf("N");}return 0;
}

一次 dfs 直接全部搜出

#include<cstdio>
#include<cstring>
#include<iostream>using namespace std;struct node {int to, nxt;
}e[8001], e_[8001], e__[8001];
int n, m, b, c, ba, ca, tmp, tot, le[2001], KK;
int dfn[2001], low[2001], in[2001], sta[2001], ru[2001];
int le_[2001], KK_, le__[2001], KK__, mst[2001];
bool inn[2001], line[2001];
char cc;void add(int x, int y) {e[++KK] = (node){y, le[x]}; le[x] = KK;
}void tarjan(int now) {dfn[now] = low[now] = ++tmp;sta[++sta[0]] = now;for (int i = le[now]; i; i = e[i].nxt)if (!dfn[e[i].to]) {tarjan(e[i].to);low[now] = min(low[now], low[e[i].to]);}else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);if (dfn[now] == low[now]) {in[now] = ++tot;while (sta[sta[0]] != now) {in[sta[sta[0]]] = tot;sta[0]--;}sta[0]--;}
}void add_(int x, int y) {e_[++KK_] = (node){y, le_[x]}; le_[x] = KK_;ru[y]++;
}void add__(int x, int y) {//这个邻接表表示这个点包含的点的反点分别在哪些点中e__[++KK__] = (node){y, le__[x]}; le__[x] = KK__;
}void dfs(int now, int ans) {inn[now] = 1;line[now] = 1;if (!ans) {for (int i = le__[now]; i; i = e__[i].nxt)if (line[e__[i].to]) {//枚举这个点包含的反向点所在的点,如果和它在一条链上你就一定要选(你是后遍历到的)ans = 1;break;}}mst[now] = ans;for (int i = le_[now]; i; i = e_[i].nxt)if (!mst[e_[i].to]) dfs(e_[i].to, ans);//你选了之后你后面的也要跟着选,所以要把 ans 传递下去line[now] = 0;
}int main() {scanf("%d %d", &n, &m);for (int i = 1; i <= m; i++) {scanf("%d", &b);cc = getchar();while (cc != 'Y' && cc != 'N') cc = getchar();if (cc == 'Y') ba = 0;else ba = 1;scanf("%d", &c);cc = getchar();while (cc != 'Y' && cc != 'N') cc = getchar();if (cc == 'Y') ca = 0;else ca = 1;add((ba ^ 1) * n + b, ca * n + c);add((ca ^ 1) * n + c, ba * n + b);}for (int i = 1; i <= n + n; i++)if (!dfn[i]) tarjan(i);for (int i = 1; i <= n; i++)if (in[i] == in[n + i]) {printf("IMPOSSIBLE");return 0;}for (int i = 1; i <= n + n; i++)for (int j = le[i]; j; j = e[j].nxt)if (in[i] != in[e[j].to])add_(in[i], in[e[j].to]);for (int i = 1; i <= n; i++)add__(in[i], in[i + n]), add__(in[i + n], in[i]);for (int i = tot; i >= 1; i--)if (!ru[i]) dfs(i, 0);for (int i = 1; i <= n; i++) {if (!mst[in[i]] && !mst[in[i + n]]) printf("?");else if (!mst[in[i + n]]) printf("Y");else printf("N");}return 0;
}

【ybt金牌导航3-6-3】【luogu P3007】奶牛议会 / The Continental Cowngress G(两种方法)相关推荐

  1. 【ybt金牌导航2-3-3】【luogu P3975】K小子串 / 弦论

    K小子串 / 弦论 题目链接:ybt金牌导航2-3-3 / luogu P3975 题目大意 给你一个字符串,要你求字典序第 k 小的子串. (相同的子串可能算一个,也可能算多个,数据以读入 0/1 ...

  2. 【ybt金牌导航8-3-3】【luogu P4593】分数计算 / 教科书般的亵渎(数学)(拉格朗日插值)

    分数计算 / 教科书般的亵渎 题目链接:ybt金牌导航8-3-3 / luogu P4593 题目大意 有一些怪,血量从 1~n,其中有 m 个数是没有怪的,给出这些数. 然后你可以每次操作攻击所有怪 ...

  3. 【ybt金牌导航4-7-3】【luogu P3437】三维俄罗斯方块/TET-Tetris 3D

    三维俄罗斯方块/TET-Tetris 3D 题目链接:ybt金牌导航4-7-3 / luogu P3437 题目大意 要你支持区间求最大,并把这个区间的所有点高度改为你求得的最大值加一个值. 最后要你 ...

  4. 【ybt金牌导航3-2-1】【luogu P3376】网络最大流【Dinic算法】

    网 络 最 大 流 网络最大流 网络最大流 题目链接:ybt金牌导航3-2-1 / luogu P3376 题目 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入 第一行包含四个正整数 ...

  5. 【luogu P4036】【ybt金牌导航4-5-3】火星人

    火星人 题目链接:luogu P4036 / ybt金牌导航4-5-3 题目大意 给你一个字符串,要你维护三个东西. 修改字符串的一个字符,往字符串的一个地方插入一个字符,询问两个后缀的最长公共前缀. ...

  6. 【启发式合并】梦幻布丁(金牌导航 启发式合并-1/luogu 3201)

    梦幻布丁 金牌导航 启发式合并-1 luogu 3201 题目大意 有若干个布丁,给出它们的颜色,每次将一个颜色的所有布丁变成另一种颜色,然后询问有多少段连续的数 输入样例 4 3 1 2 2 1 2 ...

  7. 【ybt金牌导航8-7-1】数对统计 / 关于莫比乌斯函数的少量内容

    数对统计 题目链接:ybt金牌导航8-7-1 题目大意 给你 n,m,求 gcd(x,y)=1 的数对个数. 1<=x<=n,1<=y<=m 思路 莫比乌斯函数 什么东西 首先 ...

  8. W3school导航条实现的两种方法

    W3school导航条实现的两种方法(浮动,弹性布局) 1.用浮动实现 <!DOCTYPE html> <html lang="en"> <head& ...

  9. 关于Android系统隐藏导航栏的两种方法

    关于Android系统隐藏导航栏的两种方法 在NXP的imx6q和全志的A40I上试过 1.frameworks/base/core/res/res/values/dimens.xml中修改 < ...

最新文章

  1. 字段对应数组_字段不同的多个工作薄汇总? 还要固定字段位置 !难不倒我!...
  2. Java调用WebService接口实现发送手机短信验证码功能,java 手机验证码,WebService接口调用...
  3. android类中定义颜色,自定义实现简单的Android颜色选择器(附带源码)
  4. source insight 4.0的基本使用方法
  5. pom.xml中的artifactId是什么意思?
  6. python字符串的方法和列表的方法_Python 中列表和字符串的方法总结
  7. C#生成不重复随机数列表
  8. springmvc工作流程_springMVC工作原理及流程详细讲解
  9. CentOS和AIX查看系统序列号
  10. Navicat for Oracle中如何使用外键
  11. linux内核源码分析plat-form 分析
  12. 经典算法-(六)老鼠走迷宫
  13. 为Exchange Server创建多主机名证书
  14. ajax异步数据加载
  15. javaw和java,java,javaw和javaws之间有什么区别?
  16. 电源压敏电阻的计算选择
  17. iconfont 在项目中使用阿里icon
  18. HTML中的input type=reset标签失效(不起作用)的可能原因
  19. Gnome3 快捷键
  20. Python爬虫入门教程 98-100 帮粉丝写 Python 爬虫之【2020秋季最难的事--约不上的疫苗】

热门文章

  1. TECHNETCAL日本免费空间今日崩溃
  2. 祝愿妈妈手术一切顺利
  3. CnOpenData失信企业基本信息数据
  4. 预测赢家_新敏捷—赢家
  5. ZBrush 3D游戏建模教程:创建女武士模型
  6. Winform中的双缓冲
  7. 专科应届生,小作坊公司,初次面试的一次经历
  8. 使用selenium爬取腾讯热点新闻
  9. 2020-12-11 RK系列 救砖头,强制进入maskrom模式
  10. shell程序中 2> /dev/null 代表什么意思?