题目大意
有(n)个城镇被分成了(k)个郡,有(m)条连接城镇的无向边。要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。
题目分析
每条边至少有一个端点是首都,每个郡至多一个首都,很容易想到(2-sat)判定。
考虑如何建边。我们用(x)表示在编号为(x)的节点建首都,(x')表示不在该点建首都。
对于一条边的两个端点,若左端点(l)不为首都,则右端点(r)必为首都;右端点同理,因此(l)向(r')连边,(r)向(l')连边。
对于同一个郡内的点,若(a_1)为首都,则其他点都不能为首都,按照套路我们应取郡内每一个点(a_i')向郡内其他点(a_j')连边。但这样边数是(n^2)级别的,难以接受。
于是考虑优化建边。观察同一个郡内的点集({a_1,a_2,...,a_n}),我们发现建边时,有许多重复的边(()如选择(a_3)和(a_4)作为首都,那么({a_1,a_2})以及({a_5,a_6})都不能作为首都()),因此我们很容易想到前缀与后缀优化连边。
我们新增虚节点(a_i''),并且(a_{i+1}'')向(a_{i}'')连边,(a_i'')向(a_i')连边,那么如果选择(a_i)作为首都,对于(a_j(j<i))就只需要(a_i)向(a_{i-1}'')连边即可。这是前缀优化。对于(a_j(j>i))同理用后缀优化即可。
#include <bits/stdc++.h>
using namespace std;
int getint(){
int w=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')w=w*10+ch-'0',ch=getchar();
return w;
}
const int maxn=4000005;
int n,m,k,h[maxn],dfn[maxn],low[maxn],st[maxn],bel[maxn],scc,sign,top;
bool instack[maxn];
struct edge{int to,next;}e[maxn*5];
void addedge(int x,int y){
static int cnt;
e[++cnt]=(edge){y,h[x]};h[x]=cnt;
}
struct info{int x,i,y;};
void dfs(int x){
static info S[maxn];
static int Top;
int i,y;
call:
dfn[x]=low[x]=++sign;st[++top]=x;instack[x]=1;
for(i=h[x];i;i=e[i].next){
y=e[i].to;
if(!dfn[y]){
S[++Top]=(info){x,i,y};
x=y;goto call;
Return:;
low[x]=min(low[x],low[y]);
}
else if(instack[y])low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
scc++;
for(;;){
int y=st[top--];
instack[y]=0;bel[y]=scc;
if(y==x)break;
}
}
if(Top){x=S[Top].x;i=S[Top].i;y=S[Top].y;Top--;goto Return;}
}
int main(){
// freopen("capital.in","r",stdin);
// freopen("capital.out","w",stdout);
n=getint();m=getint();k=getint();
for(int i=1;i<=m;i++){
int x=getint(),y=getint();
addedge(x+n,y);addedge(y+n,x);
}
for(int i=1;i<=k;i++){
int x=getint();
for(int j=1;j<=x;j++)st[j]=getint();
for(int j=x;j>1;j--)addedge(st[j]+2*n,st[j-1]+2*n);
for(int j=1;j<x;j++)addedge(st[j]+3*n,st[j+1]+3*n);
for(int j=1;j<=x;j++){
if(j>1)addedge(st[j],st[j-1]+2*n);
if(j<x)addedge(st[j],st[j+1]+3*n);
}
}
for(int i=1;i<=n;i++)addedge(i+2*n,i+n),addedge(i+3*n,i+n);
for(int i=1;i<=4*n;i++)if(!dfn[i])dfs(i);
bool flag=0;
for(int i=1;i<=n;i++)if(bel[i]==bel[i+n])flag=1;
if(!flag)puts("TAK");
else puts("NIE");
}