题目描述

通过一些不可描述的方式,妹滋滋算出了 51% 的得票率,于是就她就把这个公开给了广大用户 —— UOJ 解散已成定局。

几个小时后,UOJ 创始人伏特跳蚤国王宣布辞职,即日起退出 UOJ 团队。

这两个消息在算法竞赛界引起了轩然大波,“UOJ 是什么”“废除UOJ有什么影响” 马上成为了网民们的搜索热点并出现在了各大搜索网站的首页上。

著名的大水群和三连击发源地 —— Universal OJ 用户群随之解散,导致大量 OI 水狗们无处可水。一段时间后,圈子里渐渐传出了恢复 UOJ 的呼声,更有一些人将这个烂摊子归咎于那些投票通过的用户 —— 他们决定找出这些人并加以指责。

经过一段时间的搜索,他们找到了 n 个嫌疑人,编号为 1 到 n,导致 UOJ 解散的犯人就在他们之间。严刑拷打之下,他们交代了一些供词,供词有两类:

xi 说 yi 是犯人。

xi 说 yi 不是犯人。

然而,让事情变得复杂的是,犯人们并不打算背锅,所以他们的供词不总是真的,同时,为了不闹乌龙暴露自己,每一个犯人的所有供词最多有一句是假的,而不是犯人的嫌疑人的供词总是真的。

现在给出了全部的 mm 条供词,你需要找出哪些人是犯人。如果有多解,输出任何一组解即可。

输入格式

第一行两个正整数 n,m,表示犯人数目与供词数目。

接下来 m 行,每行三个整数 xi,yi,ti。其中 ti=0 表示 xi 说 yi 是犯人,ti=1 表示 xi 说 yi 不是犯人。

输出格式

第一行一个整数 c 表示犯人的数目。

第二行 c 个整数 pi,按照升序输出所有犯人的编号。

如果不存在一个犯人的集合使得供词满足条件,输出一行一个单词 "Impossible"。

题解

2-SAT。

将一个人拆成两个点,表示他是犯人和他不是犯人。若他不是犯人,那么他说的话都是对的,那么就可以通过他是犯人推出他说的人是不是犯人。如果有人说他是犯人,那么可以推出他肯定是犯人。若他是犯人,那么可以推出所有说他不是犯人的人一定是犯人。因为他只能说一句谎话,所有他说的话的反命题一定可以推出他的其他话一定是对的。然而这样的话边数是m^2的,所以用前/后缀和优化构图即可。

关于如何判无解与输出方案:

把可以推出的关系看做有向图的边,那么一个强连通分量就可以看做等价的命题。如果两个矛盾的命题是等价的,(即一个人既是犯人又不是犯人)那么就无解。

输出方案时,考虑强连通分量中的每一个点的反命题,如果反命题选了,那么这一整个强连通分量就不能选了,不然就可以选。

代码

 #include <cstdio>
#include <algorithm> #define R register
#define maxn 400010
#define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
inline int F()
{
R char ch; R int cnt = ;
while (ch = getchar(), ch < '' || ch > '') ;
cnt = ch - '';
while (ch = getchar(), ch >= '' && ch <= '') cnt = cnt * + ch - '';
return cnt;
}
struct Edge {
Edge *next;
int to;
} *last[maxn << ], e[maxn << ], *ecnt = e;
struct edge {
edge *next; int to, w;
} *lt[maxn], le[maxn << ], *lecnt = le, *rt[maxn], re[maxn << ], *recnt = re;
inline void link1(R int a, R int b, R int w)
{
*++lecnt = (edge) {lt[a], b, w}; lt[a] = lecnt;
*++recnt = (edge) {rt[b], a, w}; rt[b] = recnt;
}
inline void link(R int a, R int b)
{
// printf("%d %d\n", a, b);
*++ecnt = (Edge) {last[a], b}; last[a] = ecnt;
}
int dfn[maxn], low[maxn], timer, st[maxn], top, id[maxn], colcnt, n;
bool fail, used[maxn];
void tarjan(R int x, R int fa)
{
dfn[x] = low[x] = ++timer; st[++top] = x;
for (R Edge *iter = last[x]; iter; iter = iter -> next)
if (iter -> to != fa)
{
if (!dfn[iter -> to])
{
tarjan(iter -> to, x);
cmin(low[x], low[iter -> to]);
}
else if (!id[iter -> to]) cmin(low[x], dfn[iter -> to]);
}
if (dfn[x] == low[x])
{
++colcnt; R bool flag = ;
for (; ;)
{
R int now = st[top--];
id[now] = colcnt;
// printf("now %d colcnt %d\n", now, colcnt);
if (now <= * n)
{
flag &= !used[id[now <= n ? now + n : now - n]];
now <= n ? fail |= (id[now + n] == id[now]) : fail |= (id[now - n] == id[now]);
}
if (now == x) break;
}
used[colcnt] = flag;
}
}
int ans[maxn], tot;
int main()
{
n = F(); R int m = F();
for (R int i = ; i <= m; ++i)
{
R int a = F(), b = F(), w = F();
link1(a, b, w);
}
R int ptot = * n;
for (R int i = ; i <= n; ++i)
{
// printf("i = %d\n", i);
R int lp = ptot, rp;
for (R edge *iter = lt[i]; iter; iter = iter -> next)
{
link(i + n, iter -> to + n * iter -> w);
ptot != lp ? link(ptot + , ptot), : ;
link(++ptot, iter -> to + n * iter -> w);
}
rp = ptot;
for (R edge *iter = lt[i]; iter; iter = iter -> next)
{
if (iter != lt[i]) link(ptot, ptot + );
link(++ptot, iter -> to + n * iter -> w);
}
for (R edge *iter = rt[i]; iter; iter = iter -> next)
if (!iter -> w) link(i + n, iter -> to);
R int counter = ;
for (R edge *iter = lt[i]; iter; iter = iter -> next)
{
++counter;
if (iter != lt[i]) link(iter -> to + n * (iter -> w ^ ), lp + counter - );
if (iter -> next) link(iter -> to + n * (iter -> w ^ ), rp + counter + );
// for (R edge *iter2 = lt[i]; iter2; iter2 = iter2 -> next)
// if (!(iter -> to == iter2 -> to && iter -> w == iter2 -> w))
// {
// link(iter -> to + n * (iter -> w ^ 1), iter2 -> to + n * iter2 -> w);
// link(iter2 -> to + n * (iter2 -> w ^ 1), iter -> to + n * iter -> w);
// }
}
for (R edge *iter = rt[i]; iter; iter = iter -> next)
if (iter -> w) link(i, iter -> to);
}
for (R int i = ; !fail && i <= n; ++i) if (!dfn[i]) tarjan(i, );
if (fail)
{
puts("Impossible");
return ;
}
for (R int i = ; i <= n; ++i) if (used[id[i]]) ans[++tot] = i;
printf("%d\n", tot);
std::sort(ans + , ans + tot + );
for (R int i = ; i <= tot; ++i) printf("%d ", ans[i]);
return ;
}

【UOJ #210】【UER #6】寻找罪犯的更多相关文章

  1. UOJ#210. 【UER #6】寻找罪犯 2-sat

    #210. [UER #6]寻找罪犯 链接:http://uoj.ac/problem/210 想法:2-sat模型.每个人拆点,分别表示为犯人.非犯人.每个句供词拆点,分别表示真话.假话.供词与对应 ...

  2. uoj #210. 【UER #6】寻找罪犯【2-SAT】

    首先最直观的,列一排是罪犯一排不是罪犯,对于一个条件u说v(0是1否)f罪犯,如果u不是,那么vf罪犯:如果u是,枚举他说谎的一条wg罪犯,令w(g^1)罪犯连其他条的vf 但是这样有个电度数方,会炸 ...

  3. [UOJ210]寻找罪犯

    2-sat神题.. 告诉是2-sat我也完全想不到正解. 看了看题解其实一步步分析也不算很难 这个题首先是要围绕每个人是否是犯人和每句话是否是真话来思考 首先要明确的是: 1.好人不说谎话 2.说了谎 ...

  4. UOJ #455 [UER #8]雪灾与外卖 (贪心、模拟费用流)

    题目链接 http://uoj.ac/contest/47/problem/455 题解 模拟费用流,一个非常神奇的东西. 本题即为WC2019 laofu的讲课中的Problem 8,经典的老鼠进洞 ...

  5. [UOJ#245][UER#7]天路(近似算法)

    允许5%的相对误差,意味着我们可以只输出$\log_{1.05} V$种取值并保证答案合法.并且注意到答案随着区间长度而单增,故取值不同的答案区间是$O(\log_{1.05} V)$的. 于是初始x ...

  6. 2-sat问题学习记录

    如果你不知道什么是sat问题,请看以下问答. Q:sat问题是什麽?A:首先你有n个布尔变量,然后你有一个关于这n个布尔变量的布尔表达式,问你,如果让你随意给这n个布尔变量赋值,这个布尔表达式能否成立 ...

  7. P4478 [BJWC2018]上学路线

    Description 小B 所在的城市的道路构成了一个方形网格,它的西南角为(0,0),东北角为(N,M). 小B 家住在西南角,学校在东北角.现在有T 个路口进行施工,小B 不能通过这些路口.小B ...

  8. Tarjan/2-SAT学习笔记

    Tarjan/2-SAT Tags:图论 作业部落 评论地址 Tarjan 用来求割边或者割点,求点双联通分量或者边双联通分量 点双联通分量:两个点之间有两条点不相交的路径 边双联通分量:两个点之间有 ...

  9. Tarjan&2-SAT 总结

    \(Tarjan\)&\(2-SAT\) 标签: 知识点总结 安利XZYXZY ps:里面的部分东西来自\(Anson\)和\(yler\)和\(XZY\) 阅读体验:https://zybu ...

随机推荐

  1. CentOS7Linux中服务器LVS负载均衡、高可用集群搭建(NAT、DR);

    目录 集群 声明 集群概念 集群特性 Web服务器并发相应瓶颈 集群的分类 LB实现方法: LVS集群 负载调度器 服务器池 共享存储 LVS负载均衡的三种模式 负载均衡 集群 声明 文档不断更新中. ...

  2. <<C++ Primer>> 第 5 章 语句

    术语表 第 5 章 语句 块(block): 包围在花括号内的由 0 条或多条语句组成的序列.块也是一条语句,所以只要是能使用语句的地方,就可以使用块.    break语句(break statem ...

  3. [LeetCode] 矩形面积

    题目链接: https://leetcode-cn.com/problems/rectangle-area 难度:中等 通过率:41.3% 题目描述: 在 二维 平面上计算出两个 由直线构成的 矩形重 ...

  4. python with hadoop

    python with  hdfs hdfs 可以在 linux 本地操作 bin/hdfs dfs -ls /foo 但是这种只能在 命令行 操作. 通常我们需要在程序中实现远程操作,python ...

  5. JS判断当前页面是在 QQ客户端/微信客户端/iOS浏览器/Android浏览器/PC客户端

    browser.js var browser = { versions: function () { var u = navigator.userAgent, app = navigator.appV ...

  6. selenium与页面交互之一:webdriver浏览器的属性

    selenium提供了许多API方法与页面进行交互,如点击.键盘输入.打开关闭网页.输入文字等. webdriver对浏览器提供了很多属性来对浏览器进行操作,常用的如图: get(url).quit( ...

  7. 使用Jenkins结合Gogs和SonarQube对项目代码进行测试、部署、回滚,以及使用keepalived+haproxy调度至后端tomcat

    0 环境说明 主tomcat:192.168.0.112 备tomcat:192.168.0.183 haproxy+keepalived-1:192.168.0.156 haproxy+keepal ...

  8. Python 经典面试题(一)

    一.浮点数运算 题目 判断浮点数的运行结果是否相等:  a = 0.1 b = 0.2 c = 0.3 assert a + b == c 题目解析: 本题考查的是计算机的浮点运算知识点.不仅是 py ...

  9. Css布局 响应式布局介绍

    1. 概念: 写一套css样式可以同时适配多个终端,是为解决移动互联网诞生的. 2. 作用: 面对不同的分辨率设备灵活性强,能够快捷解决多设备显示适应问题 3. 原理 媒体查询 ① 外联式媒体查询语法 ...

  10. Echarts-主题切换

    从网上搜索了相关的方法,是主题之前的切换,但是用的是下拉框类型的,也可以设置div样式,参考官网那种 设置一个div,通过三个图片的点击效果实现切换主题的功能 我用的jQuery和Echarts是cd ...