1. 程式人生 > >bzoj 3495: PA2010 Riddle 2-sat

bzoj 3495: PA2010 Riddle 2-sat

Description

k個國家,幾個城市,m條邊。
要求每個國家有且僅有一個首都,每條邊兩端的城市至少要有一個首都。
判斷是否有解, 有解輸出“TAK”,無解輸出"NIE"
1  < =  k, N ,M , < =1000000。

       先說下輸入吧,n,m,k,然後m對關係,最後k行每行第一個數字x表示第i個國家有幾個城市,後面x個數字表示這個國家所佔有的城市。

       如果只有第一對關係,兩邊至少有一個首都,那麼很明顯就是“或”的關係了,說明如果左邊點x不選,那麼右邊點必須選,如果右邊點不選,則左邊點必須選,所以從x'向y連一條邊,y'向x連一條邊。

       最難考慮的就是每個國家只能有一個首都,第一反應就是,如果某一個選了,就會有後面的不能選,前面的也不能選的關係,但是邊數是n方的,難以接受。

      dang~dang~dang~dang~下面就是一個神奇的東西,字首優化建圖了。

      我們對於每個點,再開兩個點,表示第i個點及之前是或否有被選中過,那麼對於這樣的新點涉及的邊數應該會很少,所以我們考慮v一下如何和連邊。

       首先我們用x1表示該點被選中,x2表示該點沒被選中,x3表示該點的字首裡有被選中過的,x4表示該點的字首裡沒有被選中過的,y表示這個城市在輸入順序中的上一個城市,y1-4的定義與x的定義相同。

       首先,要是這個點選了,那麼這個點的字首裡肯定有被選中的點了,從x1向x3連一條邊。

       其次,要是這個點的字首裡沒有被選中的點,那麼這個點也一定沒有被選,從x4向x2連一條邊。

       接著,最簡單的傳遞,如果我上一個點的字首裡有點了,那麼我這個點也有,則從y3向x3連一條邊,

       然後,與上一個一樣,如果我這個點的字首裡沒有點,那麼它之前也沒有,則從x4向y4連一條邊

       還有,要是我這個點被選了,說明我上一個點及之前肯定沒有被選,則從x3向y4連一條邊,

       最後,如果這個點選了,那麼它上一個點的字首裡也不會有點,則從x1向y4連一條邊,

       最後的最後tarjan縮點判斷可行性即可,注意有兩種情況,第i個點選或不選同時在一個集合,以及第i個點的字首有沒有選點同在在一個集合,只要滿足一種就無可行解了。

       下附AC程式碼。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
#define maxn 3000005
using namespace std;
//0:選,1:不選,2:存在,3:不存在 
int n,m,k,tot,num,cnt;
int head[maxn<<2],to[6*maxn],nex[6*maxn],pre[6*maxn];
void add(int x,int y)
{
	to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int low[maxn<<2],dfn[maxn<<2],bel[maxn<<2];
bool vis[maxn<<2];
stack<int>s;
void tarjan(int now)
{
	low[now]=dfn[now]=++cnt;
	vis[now]=1;
	s.push(now);
	for(int i=head[now];i;i=nex[i])
	{
		if(!dfn[to[i]])
		{
			tarjan(to[i]);
			low[now]=min(low[now],low[to[i]]);
		}
		else if(vis[to[i]])
		{
			low[now]=min(low[now],dfn[to[i]]);
		}
	}
	if(low[now]==dfn[now])
	{
		num++;
		int temp;
		do
		{
			temp=s.top();s.pop();
			bel[temp]=num;
			vis[temp]=0;
		}while(temp!=now);
	}
	return;
}
int main()
{
	memset(pre,-1,sizeof(pre));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);x--;y--;
		int x1=((x<<2)|1),y1=((y<<2)|1);
		x<<=2;y<<=2;
		add(x1,y);add(y1,x);
	}
	for(int i=1;i<=k;i++)
	{
		int x,last;
		scanf("%d%d",&x,&last);
		last--;
		for(int j=1;j<x;j++)
		{
			int y;
			scanf("%d",&y);y--;
			pre[y]=last;last=y;
		}
	}
	for(int i=0;i<n;i++)
	{
		int x1=(i<<2),x2=(x1|1),x3=(x2+1),x4=(x3+1);
		add(x1,x3);add(x4,x2);
		if(pre[i]!=-1)
		{
			int j=pre[i];
			int y1=(j<<2),y2=(y1|1),y3=(y2+1),y4=(y3+1);
			add(y3,x3);add(x4,y4);add(y3,x2);add(x1,y4);
		}
	} 
	for (int i=0;i<(n<<2);i++)
    if (!dfn[i]) 
	tarjan(i);
    for (int i=0;i<(n<<2);i++)
    if (bel[i]==bel[i^1]) 
	{
		printf("NIE");
		return 0;
	}
	printf("TAK\n");
	return 0;
}#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stack>
#define maxn 3000005
using namespace std;
//0:選,1:不選,2:存在,3:不存在 
int n,m,k,tot,num,cnt;
int head[maxn<<2],to[6*maxn],nex[6*maxn],pre[6*maxn];
void add(int x,int y)
{
	to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int low[maxn<<2],dfn[maxn<<2],bel[maxn<<2];
bool vis[maxn<<2];
stack<int>s;
void tarjan(int now)
{
	low[now]=dfn[now]=++cnt;
	vis[now]=1;
	s.push(now);
	for(int i=head[now];i;i=nex[i])
	{
		if(!dfn[to[i]])
		{
			tarjan(to[i]);
			low[now]=min(low[now],low[to[i]]);
		}
		else if(vis[to[i]])
		{
			low[now]=min(low[now],dfn[to[i]]);
		}
	}
	if(low[now]==dfn[now])
	{
		num++;
		int temp;
		do
		{
			temp=s.top();s.pop();
			bel[temp]=num;
			vis[temp]=0;
		}while(temp!=now);
	}
	return;
}
int main()
{
	memset(pre,-1,sizeof(pre));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);x--;y--;
		int x1=((x<<2)|1),y1=((y<<2)|1);
		x<<=2;y<<=2;
		add(x1,y);add(y1,x);
	}
	for(int i=1;i<=k;i++)
	{
		int x,last;
		scanf("%d%d",&x,&last);
		last--;
		for(int j=1;j<x;j++)
		{
			int y;
			scanf("%d",&y);y--;
			pre[y]=last;last=y;
		}
	}
	for(int i=0;i<n;i++)
	{
		int x1=(i<<2),x2=(x1|1),x3=(x2+1),x4=(x3+1);
		add(x1,x3);add(x4,x2);
		if(pre[i]!=-1)
		{
			int j=pre[i];
			int y1=(j<<2),y2=(y1|1),y3=(y2+1),y4=(y3+1);
			add(y3,x3);add(x4,y4);add(y3,x2);add(x1,y4);
		}
	} 
	for (int i=0;i<(n<<2);i++)
    if (!dfn[i]) 
	tarjan(i);
    for (int i=0;i<(n<<2);i++)
    if (bel[i]==bel[i^1]) 
	{
		printf("NIE");
		return 0;
	}
	printf("TAK\n");
	return 0;
}