【UOJ #210】【UER #6】寻找罪犯
题目描述
通过一些不可描述的方式,妹滋滋算出了 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】寻找罪犯的更多相关文章
- UOJ#210. 【UER #6】寻找罪犯 2-sat
#210. [UER #6]寻找罪犯 链接:http://uoj.ac/problem/210 想法:2-sat模型.每个人拆点,分别表示为犯人.非犯人.每个句供词拆点,分别表示真话.假话.供词与对应 ...
- uoj #210. 【UER #6】寻找罪犯【2-SAT】
首先最直观的,列一排是罪犯一排不是罪犯,对于一个条件u说v(0是1否)f罪犯,如果u不是,那么vf罪犯:如果u是,枚举他说谎的一条wg罪犯,令w(g^1)罪犯连其他条的vf 但是这样有个电度数方,会炸 ...
- [UOJ210]寻找罪犯
2-sat神题.. 告诉是2-sat我也完全想不到正解. 看了看题解其实一步步分析也不算很难 这个题首先是要围绕每个人是否是犯人和每句话是否是真话来思考 首先要明确的是: 1.好人不说谎话 2.说了谎 ...
- UOJ #455 [UER #8]雪灾与外卖 (贪心、模拟费用流)
题目链接 http://uoj.ac/contest/47/problem/455 题解 模拟费用流,一个非常神奇的东西. 本题即为WC2019 laofu的讲课中的Problem 8,经典的老鼠进洞 ...
- [UOJ#245][UER#7]天路(近似算法)
允许5%的相对误差,意味着我们可以只输出$\log_{1.05} V$种取值并保证答案合法.并且注意到答案随着区间长度而单增,故取值不同的答案区间是$O(\log_{1.05} V)$的. 于是初始x ...
- 2-sat问题学习记录
如果你不知道什么是sat问题,请看以下问答. Q:sat问题是什麽?A:首先你有n个布尔变量,然后你有一个关于这n个布尔变量的布尔表达式,问你,如果让你随意给这n个布尔变量赋值,这个布尔表达式能否成立 ...
- P4478 [BJWC2018]上学路线
Description 小B 所在的城市的道路构成了一个方形网格,它的西南角为(0,0),东北角为(N,M). 小B 家住在西南角,学校在东北角.现在有T 个路口进行施工,小B 不能通过这些路口.小B ...
- Tarjan/2-SAT学习笔记
Tarjan/2-SAT Tags:图论 作业部落 评论地址 Tarjan 用来求割边或者割点,求点双联通分量或者边双联通分量 点双联通分量:两个点之间有两条点不相交的路径 边双联通分量:两个点之间有 ...
- Tarjan&2-SAT 总结
\(Tarjan\)&\(2-SAT\) 标签: 知识点总结 安利XZYXZY ps:里面的部分东西来自\(Anson\)和\(yler\)和\(XZY\) 阅读体验:https://zybu ...
随机推荐
- python 并发编程 多线程 Thread对象的其他属性或方法
介绍 Thread实例对象的方法 # isAlive(): 返回线程是否活动的. # getName(): 返回线程名. # setName(): 设置线程名. threading模块提供的一些方法: ...
- Maven从入门到精通(四)
这一篇我会着重讲解Maven的核心命令及作用,Maven在项目构建各个阶段的作用. 1.maven生命周期模型: 1.1.清洁(clean) 1.2.默认(default) 1.3.站点(site) ...
- [转帖]vCenter 计划任务.
vSphere5.0实用小技巧-DPM计划任务 2013年05月12日 23:00:09 weixin_34185320 阅读数:9 https://blog.csdn.net/weixin_3418 ...
- 洛谷 P2801 教主的魔法 题解
题面 刚看到这道题的时候用了个树状数组优化前缀和差分的常数优化竟然AC了?(这数据也太水了吧~) 本人做的第一道分块题,调试了好久好久,最后竟然没想到二分上还会出错!(一定要注意)仅此纪念: #inc ...
- 树形DP水题系列(1):FAR-FarmCraft [POI2014][luogu P3574]
题目 大意: 边权为1 使遍历树时到每个节点的时间加上点权的最大值最小 求这个最小的最大值 思路: 最优化问题 一眼树形DP 考虑状态设立 先直接以答案为状态 dp[u] 为遍历完以u为根的子树的答案 ...
- Layui数据表格的接口数据请求方式为Get
Layui数据表格的接口数据请求方式为Get
- 剑指offer-顺时针打印矩阵-数组-python
题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数 ...
- 可以提升幸福感的js小技巧(下)
4.数字 4.1 不同进制表示法 ES6中新增了不同进制的书写格式,在后台传参的时候要注意这一点. 29 // 10进制 035 // 8进制29 原来的方式 0o35 // 8进制29 ES6的方式 ...
- mybatis返回map结果集
今天突发奇想,想用mybatis返回一个map结果集,结果我就整了一下午,不过终于解决了 1.如果你确定返回的数据只有一条,你可以这样整 xml中: <select id="searc ...
- python-文件操作3(读写文件的详细操作)
f=open('my-heart','r') print(f.encoding)#返回字符编码 print(f.fileno())#返回操作系统的端口编号 print(f.seekable())#是否 ...