[NOI2017]游戏

Description
  不想打了戳链接趴

Solution
  我可能不会写2-sat
  好像去年那个智障白菜写了个“3-sat”???显然这个是个 NPC N P C 问题 (是个ZZ吧!
  于是白菜今年终于知道了用”2-sat”和二的 d d 次方算,但是居然不会写2-sat输出方案。

  就是暴枚然后2-sat,注意一定是要加反边的,算法依赖于对称性。特别的,对于某一个点强制其选一种方案,那么这条边没有反边,但是并不影响对称性的应用。(记住好了,懒得证明了
  还有一点就是要注意下限制条件u==v时要特判一下处理。二点就是先把拓补序算出来再处理,其中后面连的边是反边, u>v u − > v 表示不选 u u 就不选v,于是下传不选标记和自己的反标记即可。
  
  我是真的不会2-sat
 
Source

//2018-4-30
//miaowey
//
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define Set(a, v) memset(a, v, sizeof a)
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)

#define N (100000 + 5)

struct Con{
    int x, hx, y, hy;
}con[N];

char s[N], sid[4][2];
int n, m, d, type[N], numx[N], dig[N], col[N], line[N]; 
vector<int> G[N], g[N], scc[N];

int dfn, tp, scc_cnt, pre[N], low[N], st[N], sccno[N];

inline void Add(int u, int hu, int v, int hv){
    int ou = 6 - type[u] - hu, ov = 6 - type[v] - hv;
    hu = ou < hu, hv = ov < hv;

    G[u + hu * n].pb(v + hv * n);
}

#define v G[now][i]

void Dfs(int now){
    st[++tp] = now;
    pre[now] = low[now] = ++dfn;

    For(i, 0, G[now].size() - 1)
        if(!pre[v]){
            Dfs(v); low[now] = min(low[now], low[v]);

        }else if(!sccno[v]) low[now] = min(low[now], pre[v]);

    if(pre[now] == low[now]){
        ++scc_cnt;

        while(true){
            int u = st[tp--];
            sccno[u] = scc_cnt; scc[scc_cnt].pb(u);
            if(u == now) break;
        }
    }
}

#undef v

void Tarjan(){
    dfn = scc_cnt = 0; Set(pre, 0); Set(sccno, 0);
    For(i, 1, n << 1) if(!pre[i]){
        tp = 0; Dfs(i);
    }
}

void Put(int now, int nc){
    if(col[now]) return;
    col[now] = nc;

    For(i, 0, scc[now].size() - 1){
        int u = scc[now][i];
        u = u > n? sccno[u - n]: sccno[u + n];
        Put(u, 3 - nc);
    }

    if(nc == 2){
        For(i, 0, g[now].size() - 1) Put(g[now][i], 2);
    }
}

bool Check(){
    int u, v, hu, hv;

    Set(dig, 0); Set(col, 0);
    For(i, 1, n << 1) G[i].clear(), g[i].clear(), scc[i].clear();

    For(i, 1, m){
        u = con[i].x, hu = con[i].hx, v = con[i].y, hv = con[i].hy;

        if(u == v){
            if(hu == hv || hu == type[u]) continue;
            Add(u, hu, u, 6 - type[u] -  hu);
            continue;
        }
        if(type[u] == hu) continue;
        if(type[v] == hv){
            Add(u, hu, u, 6 - type[u] - hu); continue;
        }

        Add(u, hu, v, hv); Add(v, 6 - type[v] - hv, u, 6 - type[u] - hu);
    }

    Tarjan();
    For(i, 1, n) if(sccno[i] == sccno[n + i]) return false;

    For(i, 1, n << 1) For(j, 0, G[i].size() - 1){
        u = sccno[i], v = sccno[G[i][j]];

        if(u != v){
            g[v].pb(u); ++dig[u];
        }
    }

    int qn = 0;

    queue<int> q;
    For(i, 1, scc_cnt) if(!dig[i]) q.push(i);
    while(!q.empty()){
        int now = q.front(); q.pop();
        line[++qn] = now;

        For(i, 0, g[now].size() - 1){
            v = g[now][i]; if((--dig[v]) == 0) q.push(v);
        }
    }

    For(i, 1, qn){
        int now = line[i];
        if(col[now]) continue;
        Put(now, 1);
    }

    For(i, 1, n)
        if(col[sccno[i]] == 1) printf("%c", sid[type[i]][0]);
        else printf("%c", sid[type[i]][1]);
    puts("");

    return true;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
#endif

    scanf("%d%d%s", &n, &d, s + 1);

    d = 0;
    For(i, 1, n)
        if(s[i] == 'x') type[i] = 0, numx[++d] = i;
        else if(s[i] == 'a') type[i] = 1;
        else if(s[i] == 'b') type[i] = 2;
        else type[i] = 3;

    sid[1][0] = 'B'; sid[1][1] = 'C';
    sid[2][0] = 'A'; sid[2][1] = 'C';
    sid[3][0] = 'A'; sid[3][1] = 'B';

    int u, v; char h1[5], h2[5];

    scanf("%d", &m);
    For(i, 1, m){
        scanf("%d%s%d%s", &u, h1, &v, h2);
        con[i] = (Con){u, h1[0] - 'A' + 1, v, h2[0] - 'A' + 1};
    }

    For(i, 0, (1 << d) - 1){
        For(j, 0, d - 1) type[numx[j + 1]] = (i & (1 << j))? 1: 2;
        if(Check()) return 0;
    }

    puts("-1");

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值