Description
(n) 个点 (m) 条边的无向图被分成 (k) 个部分。每个部分包含一些点。请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点。
Solution
2-SAT 建图
点 (x) 表示 (x) 被选为关键点,(x') 表示 (x) 没有被选为关键点
对于每个部分,维护前缀和,(s_{k,i}) 表示第 (k) 个部分中的前 (i) 个点中有被选为关键点,(s_{k,i}') 亦然
于是连边有
(forall <a,b> in E, a' o b, b' o a)
(s_{k,i} o x_{i+1}' (and rev), x_{i} o s_{k,i} (and rev), s_{k,i} o 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
";
}
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();
}