2023icpc第二场网络赛c

做法 2-sat

赛时想到了2sat + 前缀和优化,但是对于每个点都要覆盖到脑袋抽了没想出来怎么建边

对于一个点如果他没被选择那么他的前一个点和后一个点是必选的, 然后就是一道非常裸的2sat + 前缀和优化 

P6378 [PA2010] Riddle(模板题)

1这个点是必选的, n这个点是必定不选的

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e6 + 5;

int n, a[N];
std::vector<int>edge[N];

//bel数组记录某个点在哪个连通块里面
int dfn[N],low[N],ins[N],bel[N],idx,cnt;
stack<int>stk;

void dfs(int u){
    dfn[u]=low[u]=++idx;
    ins[u]=true; stk.push(u);
    for(auto v:edge[u]){
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }else{
            if(ins[v])low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        cnt++;
        while(1){
            int v=stk.top(); bel[v]=cnt;
            ins[v]=false; stk.pop();
            if(v==u)break;
        }
    }
}

int main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    map<pair<int, int>, vector<int>> mp; cin >> n; 
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        if(i != 1) mp[{a[i - 1], a[i]}].push_back(i - 1);
        if(i != 1){
            if(i + 1 <= n) edge[i + n].push_back(i + 1);
            if(i - 1 >= 1) edge[i + n].push_back(i - 1);
        }
    }
    edge[1 + n].push_back(1); edge[n].push_back(2 * n);
    // 前缀和优化时这步不能放到下面的循环,因为 mp 中是不可能有点 n 的
    // 而 i -> pre[i], !pre[i] -> !i 需要每个点都满足
    for(int i = 1; i <= n; i++) {
        edge[i].push_back(i + 2 * n);
        edge[i + 3 * n].push_back(i + n);
    }
    for(auto [p, vec] : mp) {
        for(int i = 0; i < (int)vec.size(); i++) {
            int now = vec[i];
            if(i) {
                int last = vec[i - 1];
                edge[last + 2 * n].push_back(now + 2 * n);
                edge[now + 3 * n].push_back(last + 3 * n);
                edge[last + 2 * n].push_back(now + n);
                edge[now].push_back(last + 3 * n);
            }
        }
    }
    for(int i = 1; i <= 4 * n; i++) {
        if(!dfn[i]) dfs(i);
    }
    for(int i = 1; i <= n; i++) {
        if(bel[i] == bel[i + n]) {
            cout << "NO" << endl;
            return 0;
        }
    }
    vector<int> ans;
    for(int i = 1; i <= n; i++) {
        if(bel[i] < bel[i + n]) ans.push_back(i);
    }
    cout << (int)ans.size() << endl;
    for(auto i : ans) cout << i << ' ';
    return 0;
}
View Code

 

posted @ 2023-09-28 21:37  zhujio  阅读(78)  评论(0编辑  收藏  举报