Description
k个国家,几个城市,m条边。
要求每个国家有且仅有一个首都,每条边两端的城市至少要有一个首都。
判断是否有解, 有解输出“TAK”,无解输出"NIE"
1 < = k, N ,M , < =1000000。
如果只有第一对关系,两边至少有一个首都,那么很明显就是“或”的关系了,说明如果左边点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;
}