[NOI2017]游戏——2-sat

题面

  Bzoj4945

解析

   一道非常有趣的2-sat题

  一开始我就想偏了,我想给每一张地图建3个点分别代表A, B, C车,然后发现不会连边(就是菜到没看出来这道题怎么用2-sat),于是看了看思路,自己把代码搞出来,细细想来这个题还是很巧妙的

  假如每一张地图建3个点,由于 d<=8 , 近$\frac{1}{3}$的点会浪费,因此我们删去不能选的点,绝大部分地图都只有两个点了。这时先不考虑存在x的情况,于是所有地图就只有2个点,在2个点中选一个作为地图的选择,会有许多限制关系,那么很明显的2-sat了。考虑如何对题目中的四元组$(i, h_{i}, j, h_{j})$连边, 当然是分情况讨论,假设点$i$代表$h_{i}$, 点$i'$是点$i$的镜像点,点$j$与点$j'$同理, 如果$i$号点恰好不能选$h_{i}$,那就不连边;如果$i$号点可以选$h_{i}$但$j$号点不能选$h_{j}$, 那么强制让$i$号点选择除$h_{i}$以外的那个点,即$i\rightarrow i'$;如果$i$号点可以选$h_{i}$并且$j$号点可以选$h_{j}$,那么$i\rightarrow j$,$j'\rightarrow i'$(逆否命题)。至此建图结束,剩下的就是tarjan了,分类讨论的部分可以结合下面的代码看看,我写在了work函数内

  然后就是存在x的情况了,d很小,考虑暴力枚举每一张x图的车,我们已经建好了一张地图两辆车的2-sat的图,那么枚举每一张x图时要选出2辆车,由于x图是在3辆车中选一辆车,因此每一张x图有两种选法才能覆盖到跑x图的3种车,我是用的(A, C),与(B, C), 这样就没有遗漏的枚举出x图的情况了。枚举的时间是$2^{d}$ ,每一次重新建图,跑tarjan,因此总时间复杂度是$O(2^{d}(n+m))$

  Special Judge的存在所以我没过样例还是AC了

 代码:

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 50004;

template<class T>
inline void read(T &re)
{
    T fl = 1;char c;
    while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')fl=-1;
    re=c-'0';
    while((c=getchar())&&(c>='0'&&c<='9'))re=(re<<3)+(re<<1)+c-'0';
    re*=fl;
}

int n, m, d, rel[2][maxn];//A:1, B:2, C:3
char s[maxn];

int cnt, timer, top, stak[maxn<<1], dfn[maxn<<1], low[maxn<<1], bel[maxn<<1], xpos[10];
bool vis[maxn<<1];
vector<int> G[maxn<<1];

struct requir{
    int a, b;
    char ca, cb;
}req[maxn<<1];

void tarjan(int x)
{
    low[x] = dfn[x] = ++timer;
    stak[++top] = x;
    vis[x] = 1;
    for(unsigned int i = 0; i < G[x].size(); ++i)
    {
        int id = G[x][i];
        if(!dfn[id])
        {
            tarjan(id);
            low[x] = min(low[x], low[id]);
        }
        else if(vis[id])
            low[x] = min(low[x], dfn[id]);
    }
    if(dfn[x] == low[x])
    {
        cnt++;
        int t;
        do
        {
            t = stak[top--];
            vis[t] = 0;
            bel[t] = cnt;
        }
        while(t != x);
    }
}

bool work()
{
    timer = cnt = 0;
    for(int i = 1; i <= (n<<1); ++i)
    {
        bel[i] = dfn[i] = low[i] = 0;
        G[i].clear();
    }
    for(int i = 1; i <= m; ++i)
    {
        if(s[req[i].a] == req[i].ca)
            continue;
        if(s[req[i].b] == req[i].cb)
        {
            int x = (rel[1][req[i].a] + 'a' - 1 == req[i].ca);
            if(x == 1)
                G[req[i].a+n].push_back(req[i].a);
            else
                G[req[i].a].push_back(req[i].a+n);
        }
        else
        {
            int x = (rel[1][req[i].a] + 'a' - 1 == req[i].ca);
            int y = (rel[1][req[i].b] + 'a' - 1 == req[i].cb);
            if(x == 1)
            {
                if(y == 1)
                {
                    G[req[i].a+n].push_back(req[i].b+n);
                    G[req[i].b].push_back(req[i].a);
                }
                else
                {
                    G[req[i].a+n].push_back(req[i].b);
                    G[req[i].b+n].push_back(req[i].a);    
                }
            }
            else
            {
                if(y == 1)
                {
                    G[req[i].a].push_back(req[i].b+n);
                    G[req[i].b].push_back(req[i].a+n);
                }
                else
                {
                    G[req[i].a].push_back(req[i].b);
                    G[req[i].b+n].push_back(req[i].a+n);    
                }
            }
        }
    }
    for(int i = 1; i <= (n<<1); ++i)
        if(!dfn[i])
            tarjan(i);
    for(int i = 1; i <= n; ++i)
        if(bel[i] == bel[i+n])
            return 0;
    return 1;
}

void dfs(int x)
{
    if(x > d)
    {
        bool fl = work();
        if(fl)
        {
            for(int i = 1; i <= n; ++i)
                printf("%c", rel[bel[i] > bel[i+n]][i] + 'A' - 1);
            exit(0);
        }
        else 
            return;
    }
    for(int i = 1; i <= 2; ++i)
    {
        rel[0][xpos[x]] = 3 - i;
        rel[1][xpos[x]] = 3;
        s[xpos[x]] = 'a' + i - 1;
        dfs(x+1);
    }
}

int main()
{
    read(n);read(d);
    scanf("%s", s+1);
    d = 0;
    for(int i = 1; i <= n; ++i)
    {
        if(s[i] == 'x')
            xpos[++d] = i;
        else if(s[i] == 'a')
        {
            rel[0][i] = 2;
            rel[1][i] = 3;
        }
        else if(s[i] == 'b')
        {
            rel[0][i] = 1;
            rel[1][i] = 3;
        }
        else
        {
            rel[0][i] = 1;
            rel[1][i] = 2;
        }
    }
    read(m);
    for(int i = 1; i <= m; ++i)
    {
        char ch[4];
        read(req[i].a);
        scanf("%s", ch);
        req[i].ca = ch[0] - 'A' + 'a';
        read(req[i].b);
        scanf("%s", ch);
        req[i].cb = ch[0] - 'A' + 'a';
    }
    dfs(1);
    printf("-1");
    return 0;
}
View Code

 

posted @ 2019-08-08 01:03  Mr_Joker  阅读(204)  评论(0编辑  收藏  举报