【BZOJ3495】PA2010 Riddle

题目大意

\(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");
}
posted @ 2018-09-14 13:29  Trrui  阅读(229)  评论(0编辑  收藏  举报