Codeforces Round #441 (Div. 2, by Moscow Team Olympiad) E. National Property (2-SAT问题)

第一次接触2-sat,突然感觉自己学的离散数学和一些数学中关于逻辑的东西有了用处,开始我在做这个题的时候,我在想应该怎么做呢,如果能有一个判断逻辑的算法,而且能直接算出他们的解或者推翻自己的逻辑输出错误,那该有多好。赛后题解就发现了2-sat。我却有懒得要命,只想会用,连原理都不想弄通,多嘲讽啊。即将去比赛是理由?


【题意】对于一个数字如3,那么3'<3,且加了'的数均小于没有加'的数,对于两个加了'的数,按照没有'时的规则比较大小,问存不存在一种修改方案将给定的一系列打次化为字典序递增的。

建图:令上一个大小为a,这一个大小为b,如果a>b 肯定让a变为a‘ 但是b’也要和b连一条线,以免后面让b变为b’。如果a<b  a要和b连一条线 a’和b’也要连一条线,以免后面会有b和a连线。也是一条逻辑。

#include <bits/stdc++.h>
#define met(a,b) memset(a,b,sizeof a)
#define pb push_back
#define mp make_pair
using namespace std;
const int N = 2e5+50;;
const int M = 2e5+50;
vector<int>vec[N],ans;
struct Edge {
    int to,next;
} edge[M];
int head[M],tot;
void init() {
    tot = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v) {
    //printf("!!!%d %d\n",u,v);
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
bool vis[N];//染色标记,为true表示选择
int S[N],top;//栈
bool dfs(int u) {
    if(vis[u^1])return false;
    if(vis[u])return true;
    vis[u] = true;
    S[top++] = u;
    for(int i = head[u]; i != -1; i = edge[i].next)
        if(!dfs(edge[i].to))
            return false;
    return true;
}
bool Twosat(int n) {
    memset(vis,false,sizeof(vis));
    for(int i = 0; i < n; i += 2) {
        if(vis[i] || vis[i^1])continue;
        top = 0;
        if(!dfs(i)) {
            while(top)vis[S[--top]] = false;
            if(!dfs(i^1)) return false;
        }
    }
    return true;
}
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    init();
    for(int i=1,s,x;i<=n;i++){
        scanf("%d",&s);
        while(s--){
            scanf("%d",&x);
            vec[i].pb(x);
        }
        if(i==1)continue;
        int len=min(vec[i].size(),vec[i-1].size());
        int pos=-1;
        for(int j=0;j<len;j++){
            if(vec[i][j]!=vec[i-1][j]){
                pos=j;break;
            }
        }
        if(pos==-1){
            if(vec[i-1].size()>vec[i].size()){
                return puts("No")*0;
            }
        }
        else {
            int a=vec[i-1][pos],b=vec[i][pos];
            if(a>b){
                addedge(2*a-1,2*a-2);
                addedge(2*b-2,2*b-1);
            }
            else {
                addedge(2*a-1,2*b-1);
                addedge(2*b-2,2*a-2);
            }
        }
    }
    if(Twosat(2*m)) {
        puts("Yes");
        for(int i = 0; i < 2*m; i+=2)
            if(vis[i])
                ans.pb((i)/2+1);
       printf("%d\n",ans.size());
       for(int x : ans)printf("%d ",x);
    } else printf("No\n");
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值