题目描述

通过一些不可描述的方式,妹滋滋算出了 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. selenium-模拟鼠标

    需要导入的包: from selenium.webdriver import ActionChains 一.模拟鼠标右键 ActionChains(self.driver).context_click ...

  2. jquery validate 自定义校验方法

    1.引入JS jquery.min.js jquery.validate.min.js messages_zh.min.js 2.添加验证方法,第一个参数为验证方法的名称,第二个参数为验证方法. $. ...

  3. Maven - Maven3实战学习笔记(1)Maven使用入门

    1.maven安装 1>http://maven.apache.org/download.cgi下载apache-maven-3.6.1 2>解压缩安装包到指定的文件夹,如C:\fyliu ...

  4. SQL 生成表结构表数据脚本

    数据库右击——>任务——>生成脚本——>选择表 ——>高级——>要编写脚本的数据的类型(架构和数据.仅限架构.仅限数据)

  5. 【一个蒟蒻的挣扎】LCA (倍增)

    #include<cstdio> #include<iostream> #include<cstring> using namespace std; struct ...

  6. algorithm下的常用函数

    algorithm下的常用函数 max(),min(),abs() max(x,y)返回x和y中最小的数字 min(x,y)返回x和y中最大的数字 abs(x)返回x的绝对值,注意x应当是整数,如果是 ...

  7. 洛谷 P2398 GCD SUM 题解

    题面 挺有意思的. 设f[i]表示gcd(i,j)=i的个数,g[i]表示k|gcd(i,j)的个数; g[i]=(n/i)*(n/i); g[i]=f[i]+f[2i]+f[3i]+...; 所以f ...

  8. c++ vector 使用注意事项

    1. 初始化 c++ 11以后新增了大括号{}的初始化方式,需要注意与()的区别,如: std::vector<int> vecTest1(5);         //初始化5个元素,每个 ...

  9. windows 2008 创建域服务器问题 账户密码不符合要求

    windows 2008新建域时,本地administrator账户将成域Administrator账户.无法新建域,因为本地administrator账户密码不符合要求.*解决办法:很多人都会想到在 ...

  10. qt对plot柱状图颜色设置

      当使用qwtplotbarchart来使用柱状图时.可以通过下面代码来设置柱状图的颜色 QwtPlotBarChart  *barChart = new QwtPlotBarChart(" ...