• UOJ #210. 【UER #6】寻找罪犯


    Description

    传送门


    Solution

    考虑朴素的(2-SAT),开点记录某个人是不是犯人,每句话是不是真的,然后每个人所说的所有话之间都要连边为保证只有一句假话。这样边数会很大,复杂度会假。

    用前缀和的思想优化该做法,(pre_{i, j})表示(i)的前(j)句话有没有出现假话。

    那么如果第(i)句话是真的,就能确定对应的(y)的身份,反之,根据(y)的身份能确定(i)是假话。

    如果之前所有说过的话有假话,那第(i)句一定是真话,反之,第(i)句是假话,则之前的一定都是真话。

    如果之前所有说过的话中有假话,加上第(i)句话的所有话中肯定有假话,反之,如果加上第(i)句话的所有话中没有假话,那么去掉第(i)句话也没有假话。

    如果第(i)句话是假话,那么从开头到现在的所有话中有假话,反之,如果从开头到现在都是真话,那么低(i)句话是真话。

    然后如果一个人所有说的话中有假话,那么他是犯人,反之,他所有说过的话中肯定没有假话。

    然后这样连边,边数是(O(m))级别的,可以通过本题。


    Code

    /*
        _______                       ________                        _______
       / _____                      / ______                       / _____ 
      / /     \_  _     __     _   / /          _     __     _   / /     \_
     | |          | |   |  |   | | | |        | | | |   |  |   | | | |
     | |          | |   |  |   | | | |     __ | | | |   |  |   | | | |
     | |       __     |  |  /  / | |      | |     |  |  /  / | |       __
       \_____/ /    / / /  /    \_____  /     / / /  /    \_____/ /
       \_______/    \___/  \___/     \______/\__   \___/  \___/     \_______/
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    
    const int N = 6e5;
    
    int cnt = 1, n, m, top, dfx, tot, num;
    
    int head[N + 50], id[N + 50], idask[N + 50], idpre[N + 50], lst[N + 50], scc[N + 50], dfn[N + 50], st[N + 50], low[N + 50];
    
    vector<int> crimer;
    
    struct Node
    {
    	int next, to;
    } edge[N * 8 + 50];
    
    void Addedge(int u, int v)
    {
    //	cout << endl;
    //	cout << u << " " << v << endl;
    	edge[++num] = (Node){head[u], v};
    	head[u] = num;
    	return;
    }
    
    void Link(int u, int v)
    {
    	Addedge(u, v);
    	Addedge(v ^ 1, u ^ 1);
    	return;
    }
    
    void Tarjan(int x)
    {
    	dfn[x] = low[x] = ++dfx; st[++top] = x;
    	for (int i = head[x]; i; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		if (!dfn[v])
    		{
    			Tarjan(v);
    			low[x] = min(low[x], low[v]);
    		}
    		else if (!scc[v]) low[x] = min(low[x], dfn[v]);
    	}
    	if (low[x] == dfn[x])
    	{
    		tot++;
    		while (st[top + 1] != x) scc[st[top--]] = tot;
    	}
    	return;
    }
    
    inline char nc(){
        #define SIZE 1000000+3
        static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
        if(p1 == p2){
            p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
            if(p1 == p2) return -1;
        }
        return *p1++;
        #undef SIZE
    }
    
    template <typename T>
    inline void read(T &x){
        x = 0;int flag = 0;char ch = nc();
        while(!isdigit(ch)){
            if(ch == '-') flag = 1;
            ch = nc();
        }
        while(isdigit(ch)){
            x = (x<<1) + (x<<3) + (ch^'0');
            ch = nc();
        }
        if(flag) x = -x;
    }
    
    int main()
    {
    	read(n); read(m);
    	for (int i = 1; i <= n; i++) cnt += 2, id[i] = cnt;
    	for (int i = 1; i <= m; i++) cnt += 2, idask[i] = cnt, cnt += 2, idpre[i] = cnt;
    	for (int i = 1, x, y, t; i <= m; i++)
    	{
    		read(x); read(y); read(t);	
    		if (lst[x]) Link(lst[x] ^ 1, idpre[i] ^ 1), Link(lst[x] ^ 1, idask[i]);//如果x之前说过话,那么如果这句话是假的,那么x之前说过的话都是真的;如果之前说过的话都是假的,那么加上现在的话也肯定有假的。 
    		lst[x] = idpre[i];
    		Link(idask[i] ^ 1, idpre[i] ^ 1);//如果这句话是假的,那么从开头到这句话都是假的。 
    		if (!t) Link(idask[i], id[y] ^ 1); else Link(idask[i], id[y]);//如果这句话是真的,那么y是罪犯/好人。 
    	}	
    	for (int i = 1; i <= n; i++) if (lst[i]) Link(lst[i] ^ 1, id[i] ^ 1);//如果所有的话中有假的,那么i是罪犯。 
    	for (int i = 2; i <= cnt; i++) if (!scc[i]) Tarjan(i); 
    	for (int i = 1; i <= n; i++) if (scc[id[i]] == scc[id[i] ^ 1]) { puts("Impossible"); return 0;}
    	for (int i = 1; i <= n; i++) if (scc[id[i]] > scc[id[i] ^ 1]) crimer.push_back(i);
    	printf("%d
    ", crimer.size());
    	for (vector<int>::iterator it = crimer.begin(); it != crimer.end(); it++) printf("%d ", *it);
    	return 0;
    }
    
  • 相关阅读:
    [t]淘宝幻灯片上下滑动效果
    [t]手风琴效果
    [t]仿FLASH的图片轮换效果
    [t]新浪微博自动加载信息
    图片加载问题
    [t]照片墙
    Best Of My Renderings
    My car renderings
    Silverlight后台CS代码中创建四种常用的动画效果
    Silverlight、SVG、WPF转换工具
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13917145.html
Copyright © 2020-2023  润新知