• 题解 洛谷 P6378 【[PA2010]Riddle】


    首先不难看出对于本题的点与点之间的限制关系,我们可以考虑用(2-SAT)来解决,通过从状态(x)向状态(y)连一条有向边表示若状态(x)存在,那么状态(y)必须存在。

    接下来的处理中,(x)表示点(x)被选为关键点,(x^prime)表示点(x)没有选为关键点,(x longrightarrow y)表示两个状态之间连一条有向边。

    对于每条边至少有一个端点是关键点这一限制,若边两端为(x)(y),那么直接进行(x^prime longrightarrow y , y^prime longrightarrow x)即可。

    处理使得每个部分恰有一个关键点这一限制时,发现要进行像一个点向该部分中除它以外的所有点连边的操作,这样的话,边数会达到(n^2)级别,这是不可以接受的,所以要考虑优化建图。

    对于每个点,我们这里在其所在的部分中考虑,每个点用(a_i)来表示,再对其新增一个状态(pre_i),表示在该部分中前(i)个点是否有点被选为关键点。

    连边时我们可以进行以下的操作:

    (a_i longrightarrow pre_{a_i} , pre_{a_i}^prime longrightarrow a_i^prime)

    $pre_{a_{i-1}} longrightarrow pre_{a_i} , pre_{a_i}^prime longrightarrow pre_{a_{i-1}}^prime $

    ( pre_{a_{i-1}} longrightarrow a_i^prime , a_i longrightarrow pre_{a_{i-1}}^prime)

    通过这样的前缀优化建图,就不用再连那么多边了,同时题目的限制条件也都可以满足。

    具体实现细节看代码吧。

    (code:)

    #include<bits/stdc++.h>
    #define p1(x) x
    #define p0(x) x+n
    #define pre1(x) x+2*n
    #define pre0(x) x+3*n
    #define maxn 8000010
    using namespace std;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int n,m,k,dfn_cnt,co_cnt,top;
    int dfn[maxn],low[maxn],co[maxn],st[maxn],a[maxn];
    bool vis[maxn];
    struct edge
    {
        int to,nxt;
    }e[maxn];
    int head[maxn],edge_cnt;
    void add(int from,int to)
    {
        e[++edge_cnt]=(edge){to,head[from]};
        head[from]=edge_cnt;
    }
    void tarjan(int x)
    {
        dfn[x]=low[x]=++dfn_cnt;
        st[++top]=x,vis[x]=true;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;
            if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
            else if(vis[y]) low[x]=min(low[x],dfn[y]);
        }
        if(low[x]==dfn[x])
        {
            co_cnt++;
            int now;
            do
            {
                now=st[top--];
                vis[now]=false;
                co[now]=co_cnt;
            }while(now!=x);
        }
    }
    bool check()
    {
        for(int i=1;i<=4*n;++i)
            if(!dfn[i])
                tarjan(i);
        for(int i=1;i<=n;++i)
            if(co[p1(i)]==co[p0(i)]||co[pre1(i)]==co[pre0(i)])
                return false;
        return true;
    }
    int main()
    {
        read(n),read(m),read(k);
        for(int i=1;i<=m;++i)
        {
            int x,y;
            read(x),read(y);
            add(p0(x),p1(y)),add(p0(y),p1(x));
        }
        for(int i=1;i<=k;++i)
        {
            int w;
            read(w);
            for(int j=1;j<=w;++j)
                read(a[j]),add(p1(a[j]),pre1(a[j])),add(pre0(a[j]),p0(a[j]));
            for(int j=2;j<=w;++j)
            {
                add(pre1(a[j-1]),pre1(a[j])),add(pre0(a[j]),pre0(a[j-1]));
                add(pre1(a[j-1]),p0(a[j])),add(p1(a[j]),pre0(a[j-1]));
            }
        }
        if(check()) puts("TAK");
        else puts("NIE");
        return 0;
    }
    
  • 相关阅读:
    1015: C语言程序设计教程(第三版)课后习题6.5
    1014 C语言程序设计教程(第三版)课后习题6.4
    1013: C语言程序设计教程(第三版)课后习题6.3
    1012: C语言程序设计教程(第三版)课后习题6.2
    1011 C语言程序设计教程(第三版)课后习题6.1
    链表结点的交换
    int、long、long long取值范围
    windows下XAMPP安装php_memcache扩展
    在VC6.0++ 下的调试
    ubuntu 安装tomcat
  • 原文地址:https://www.cnblogs.com/lhm-/p/12695332.html
Copyright © 2020-2023  润新知