\(n\) 个点 \(m\) 条边的无向图被分成 \(k\) 个部分。每个部分包含一些点。请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。
2-SAT 建图
点 \(x\) 表示 \(x\) 被选为关键点,\(x‘\) 表示 \(x\) 没有被选为关键点
对于每个部分,维护前缀和,\(s_{k,i}\) 表示第 \(k\) 个部分中的前 \(i\) 个点中有被选为关键点,\(s_{k,i}‘\) 亦然
于是连边有
\(\forall <a,b> \in E, a‘ \to b, b‘ \to a\)
\(s_{k,i} \to x_{i+1}‘ (and \ rev), x_{i} \to s_{k,i} (and \ rev), s_{k,i} \to s_{k,i+1} (and \ rev)\)
#include <bits/stdc++.h>
using namespace std;
#define reset(a) memset((a),0,sizeof((a)))
const int N = 16000005;
struct edge{
int to,next;
}e[N];
int head[N],tot,HEAD[N];
int n,m,cnt,turn[N],belong[N],vis[N];
void make(int x,int y){
e[++tot].to=y;e[tot].next=head[x];head[x]=tot;
e[++tot].to=x;e[tot].next=HEAD[y];HEAD[y]=tot;
}
void dfs1(int u){
int i;
vis[u]=1;
for(i=head[u];i;i=e[i].next)
if(!vis[e[i].to])
dfs1(e[i].to);
turn[++cnt]=u;
}
void dfs2(int u){
belong[u]=cnt;vis[u]=1;
for(int i=HEAD[u];i;i=e[i].next)
if(!vis[e[i].to])
dfs2(e[i].to);
}
void kosaraju(){
for(int i=1;i<=4*n;i++)
if(!vis[i])dfs1(i);
reset(vis);cnt=0;
for(int i=4*n;i>=1;i--)
if(!vis[turn[i]])
cnt++,dfs2(turn[i]);
}
void solve(){
kosaraju();
for(int i=1;i<=n;i++)
if(belong[i]==belong[i+n]){
cout<<"NIE";return;
}
cout<<"TAK\n";
}
int k,t;
int id(int i)
{
return i;
}
int id1(int i)
{
return n+i;
}
int ids(int i)
{
return n+n+i;
}
int ids1(int i)
{
return n+n+n+i;
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m>>k;
for(int i=1;i<=m;i++)
{
int t1,t2;
cin>>t1>>t2;
int a=t1,b=t2;
make(id1(a),id(b));
make(id1(b),id(a));
}
for(int i=1;i<=k;i++)
{
cin>>t;
vector <int> vec;
int tmp;
for(int j=0;j<t;j++)
{
cin>>tmp;
vec.push_back(tmp);
}
for(int j=0;j<t-1;j++)
{
make(ids(vec[j]), id1(vec[j+1]));
make(id(vec[j+1]), ids1(vec[j]));
make(id(vec[j]), ids(vec[j]));
make(ids1(vec[j]),id1(vec[j]));
make(ids(vec[j]), ids(vec[j+1]));
make(ids1(vec[j+1]), ids1(vec[j]));
}
}
solve();
}
原文:https://www.cnblogs.com/mollnn/p/13290614.html