测试地址:Ikki’s Story IV - Panda’s Trick
题目大意:
做法:我的博客已经持续更新一周年了,可喜可贺可喜可贺……
至于为什么这一个月没有更新呢……说来话长,你们只需要知道我NOI2017考崩了就好(T_T)。
这一题是一个2-SAT的模型。
什么叫2-SAT?2-SAT是2-适定性问题的简称,差不多是这样一个模型:给若干个东西,这些东西两两之间可能有限制条件,问有没有满足所有条件的选法。之所以命名为2-SAT就是因为这些限制条件是两两之间的条件,而k-SAT问题在k>2时已经被证明是NPC问题,只有2-SAT有比较优美的解法。
至于解2-SAT,网上有很多教程,这里就不细讲了,大概就是将每个东西分成两点,分别表示取或不取这个东西(以下称这两个点为A和非A),那么问题就转化为在每组两个点里取且仅取一个点,问有没有满足限制条件的取法。我们可以把两两之间的限制条件变成有向边的形式,通常有向边A->B表示要取A必须也取B,经过一些转化后就可以求解,主要是用强连通分量,这里就不再赘述了。
那么这一题的模型要转化为2-SAT十分简单,每条边有连里边和连外边两种选择,且只能选择一种,这已经提示了这题是2-SAT。再看限制条件,两条边不能在点外相交。经过探究,两条边在点外相交的充要条件是:两条边没有公共顶点并连在同一侧,且其中一条边的两个端点分别处在另一条边将环分成的两个部分。于是我们判断两条边是不是满足了“没有公共顶点”和最后那个条件(太长懒得写),如果同时满足,那么就说明它们的连法受到限制:不能连在环形的同侧,也就是说,对于冲突的两条边A,B,我们不能同时选择点A和B,也不能同时选点非A和非B。那么应该怎么连边呢?很简单,点A,B不能同时被选,那么就表示选了A就必须选非B,选了B就必须选非A,按照这样的转化连边即可。建完图之后,求图的强连通分量,如果在同一组的两个点处在同一个强连通分量,那么问题无解,否则问题肯定有解。其实可以求出任意一个解,但这题只需要判断有没有解,所以就下次再说啦~
总的时间复杂度:
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,a[510],b[510],tot=0,tim=0,tott,first[2010]={0};
struct {int v,next;} e[2000010];
int dfn[1010],low[1010],belong[1010]={0},stack[1010]={0},top=0;
int in[2010]={0};
bool vis[1010]={0};
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void dfs(int v)
{
vis[v]=1;
dfn[v]=++tim;
low[v]=dfn[v];
stack[++top]=v;
int now=top;
for(int i=first[v];i;i=e[i].next)
{
if (!vis[e[i].v])
{
dfs(e[i].v);
low[v]=min(low[v],low[e[i].v]);
}
else if (!belong[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
}
if (low[v]==dfn[v])
{
tott++;
for(int i=now;i<=top;i++)
belong[stack[i]]=tott;
top=now-1;
}
}
void tarjan()
{
for(int i=0;i<2*m;i++)
if (!vis[i]) dfs(i);
tott++;
for(int i=1;i<=top;i++)
belong[stack[i]]=tott;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&a[i],&b[i]);
if (a[i]>b[i]) swap(a[i],b[i]);
for(int j=0;j<i;j++)
{
if (a[i]==a[j]||b[i]==a[j]||a[i]==b[j]||b[i]==b[j]) continue;
bool f1=(a[j]>a[i]&&a[j]<b[i]),f2=(b[j]>a[i]&&b[j]<b[i]);
if (f1!=f2)
{
insert(2*j,2*i+1);
insert(2*j+1,2*i);
insert(2*i,2*j+1);
insert(2*i+1,2*j);
}
}
}
tott=2*m-1;
tarjan();
for(int i=0;i<m;i++)
if (belong[2*i]==belong[2*i+1])
{
printf("the evil panda is lying again");
return 0;
}
printf("panda is telling the truth...");
return 0;
}