Description
有n个城镇被分成了k个郡,有m条连接城镇的无向边。要求给每个郡选择一个城镇作为首都,满足每条边至少有一个端点是首都。
Input
第一行有三个整数,城镇数n(1<=n<=10^6),边数m(0<=m<=10^6),郡数k(1<=k<=n)。
接下来m行,每行有两个整数ai和bi(ai≠bi),表示有一条无向边连接城镇ai和bi。
接下来k行,第j行以一个整数wj开头,后面是wj个整数,表示第j个郡包含的城镇。
Output
若有解输出TAK,否则输出NIE。
每个点 $x$ 拆成两对点,$x$ 代表选择 $x$ 为首都,$x+n$ 表示不选择 $x$ 为首都,$x+2n$ 表示 $x$ 的前缀已包含首都,$x+3n$表示 $x$ 的前缀不包含首都。
对于每一条原图中无向边 $(x,y)$ ,因为至少有一个端点为首都,连边 $(x+n,y)$ ,$(y+n,x)$。
对于每一个点 $x$ ,连边 $(x,x+2n)$ ,$(x+3n,x+n)$。
对于每一个点 $x$ 与它的上一个点 $last$ ,连边方式如下:$(last+2n,x+2n)$,$(x+3n,last+3n)$,$(last+2n,x+n)$,$(x,last+3n)$。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=4e6+;
int n,m,k,cnt,x,y,last,tim,top,color;
int first[N],dfn[N],low[N],sta[N],c[N];
bool vis[N];
struct edge{int to,next;}e[N*];
void ins(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;}
int read()
{
int x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
void tarjan(int x)
{
low[x]=dfn[x]=++tim;
sta[++top]=x;vis[x]=true;
for(int i=first[x];i;i=e[i].next)
{
int to=e[i].to;
if(!dfn[to])tarjan(to),low[x]=min(low[x],low[to]=min(low[x],low[to]));
else if(vis[to])low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x])
{
color++;
while(sta[top]!=x)vis[sta[top]]=false,c[sta[top--]]=color;
vis[x]=false;c[x]=color;top--;
}
}
bool check()
{
for(int i=;i<=n;i++)
if(c[i]==c[i+n]||c[i+*n]==c[i+*n])return false;
return true;
}
int main()
{
n=read();m=read();k=read();
for(int i=;i<=m;i++)
{
x=read();y=read();
ins(x+n,y);ins(y+n,x);
}
for(int i=;i<=k;i++)
{
x=read();last=;
for(int j=;j<=x;j++)
{
y=read();
ins(y,y+*n);ins(y+*n,y+n);
if(last)
{
ins(last+*n,y+*n);
ins(y+*n,last+*n);
ins(last+*n,y+n);
ins(y,last+*n);
}
last=y;
}
}
for(int i=;i<=*n;i++)if(!dfn[i])tarjan(i);
if(check())printf("TAK");
else printf("NIE");
return ;
}