做法 2-sat
赛时想到了2sat + 前缀和优化,但是对于每个点都要覆盖到脑袋抽了没想出来怎么建边
对于一个点如果他没被选择那么他的前一个点和后一个点是必选的, 然后就是一道非常裸的2sat + 前缀和优化
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
标签:第二场,int,网络,back,2023icpc,dfn,low,push,ins From: https://www.cnblogs.com/zhujio/p/17736541.html