[PA2010]Riddle

Posted by Dispwnl on February 14, 2019

题目

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。

Sample Input

6 5 2
1 2
3 1
1 4
5 2
6 2
3 3 4 2
3 1 6 5

Sample Output

TAK

题解

发现有两种限制,边的限制可以转换成两个条件二选一,关键是首都的限制怎么搞

有一种神奇的优化建图方式,前(后)缀优化建图

对于一个郡,点$p_x$表示$x$前面的点(包括$x$)有选作首都的了,同时建出反点$p_x’$表示没有选作首都的点

同时$d_x$表示选择这个点当首都,$d_x’$表示不选

这样的话就有以下关系了:

边$(u,v)​$:$d_u’\rightarrow d_v,d_v’\rightarrow d_u​$

$d_x\rightarrow p_x,p_x’\rightarrow d_x’$

$p_{x-1}\rightarrow p_x,p_x’\rightarrow p_{x-1}’$

$d_x\rightarrow p_{x-1}’,p_{x-1}\rightarrow d_x’​$

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define A(x) (x)
# define B(x) (x+n)
# define C(x) (x+2*n)
# define D(x) (x+3*n)
using namespace std;
const int MAX=5e6+5;
struct p{
	int x,y;
}c[MAX<<1];
int n,m,num,Top,cnt,K,_;
int h[MAX],dfn[MAX],low[MAX],st[MAX],col[MAX],a[MAX];
bool use[MAX];
void add(int x,int y) {c[++num]=(p){h[x],y},h[x]=num;}
void Tarjan(int x)
{
	st[++Top]=x,use[x]=1,dfn[x]=low[x]=++cnt;
	for(int i=h[x];i;i=c[i].x)
	  if(!dfn[c[i].y]) Tarjan(c[i].y),low[x]=min(low[x],low[c[i].y]);
	  else if(use[c[i].y]) low[x]=min(low[x],dfn[c[i].y]);
	if(dfn[x]==low[x])
	{
		int tt=-1;
		++_;
		while(tt!=x) tt=st[Top--],use[tt]=0,col[tt]=_;
	}
}
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
int main()
{
	n=read(),m=read(),K=read();
	for(int i=1,x,y;i<=m;++i)
	  x=read(),y=read(),add(B(x),A(y)),add(B(y),A(x));
	for(int i=1;i<=n;++i)
	  add(A(i),C(i)),add(D(i),B(i));
	for(int i=1,tt;i<=K;++i)
	  {
	  	tt=read();
	  	for(int j=1;j<=tt;++j)
	  	  {
	  	  	a[j]=read();
	  	  	if(j>1) add(A(a[j]),D(a[j-1])),add(C(a[j-1]),B(a[j])),add(C(a[j-1]),C(a[j])),add(D(a[j]),D(a[j-1]));
		  }
	  }
	for(int i=1;i<=4*n;++i)
	  if(!dfn[i]) Tarjan(i);
	for(int i=1;i<=n;++i)
	  if(col[A(i)]==col[B(i)]||col[C(i)]==col[D(i)]) return printf("NIE"),0;
	return printf("TAK"),0;
}