题意
给出一个 n n 的点的环,条线,每条线连接环上的点。线可以从环内部连接也可以从环外部连接。求能否使得这 m m 条线不相交。
题解
首先要知道一个引理:如果两条线从环都从内部连接相交的话,那么他们从外部连接一样会相交。
有了这个引理,为了让两条原本相交的线变得不相交,就必须然们一条从内部连接,一条从外部连接。
设布尔变量
a、b
a
、
b
表示直线
la、lb
l
a
、
l
b
的连接情况,当其取1的时候表示从内部连接,取0表示从外部连接。
根据上面的推理,就有
a∨b=1,a∧b=0
a
∨
b
=
1
,
a
∧
b
=
0
的逻辑表示。
通俗来讲,就是两条线一条从外部连接,一条从内部连接。
再推理,就可以得到:
- 如果 la l a 从外部,那么 lb l b 一定从内部,反之也成立。
- 如果 lb l b 从外部,那么 la l a 一定从内部,反之也成立。
建立2-sta的模型,用4条有向边来表示上面的逻辑推理。
之后利用tarjan算法缩点,因为题目只需要判断是否有解,所以只需要判断原变量和反变量是否在一个强连通分量中,若在的话,问题无解,反之一定有解。
最后一点,如何判断两条线是否相交呢?这个问题其实画画就出来了。
设
lax、lay、lbx、lby
l
a
x
、
l
a
y
、
l
b
x
、
l
b
y
分别表示两条线的两个端点编号,并且有
lix≤liy
l
i
x
≤
l
i
y
。
如果有下面几种情况之一,即可判断两条直线相交:
- lax≤lbx且lay≥lbx且lay≤lby l a x ≤ l b x 且 l a y ≥ l b x 且 l a y ≤ l b y
- lbx≤lax且lby≥lax且lby≤lay l b x ≤ l a x 且 l b y ≥ l a x 且 l b y ≤ l a y
- lax≥lbx且lax≤lby且lay≥lby l a x ≥ l b x 且 l a x ≤ l b y 且 l a y ≥ l b y
- lbx≥lax且lbx≤lay且lby≥lay l b x ≥ l a x 且 l b x ≤ l a y 且 l b y ≥ l a y
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e5 + 7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
int n, m, cnt;
typedef pair<int , int > pii;
pii line[nmax];
struct edge {
int to, nxt;
};
struct graph {
int tot, head[nmax];
edge e[(int)1e6 + 7];
void init() {
tot = 0;
memset(head, -1, sizeof head);
}
void add_edge(int u, int v) {
e[tot].to = v;
e[tot].nxt = head[u];
head[u] = tot ++;
}
}g;
inline bool checkcross(pii a, pii b) {
if(a.first <= b.first && a.second >= b.first && a.second <= b.second)
return true;
else if(b.first <= a.first && b.second >= a.first && b.second <= a.second)
return true;
else if(a.first >= b.first && a.first <= b.second && a.second >= b.second)
return true;
else if(b.first >= a.first && b.first <= a.second && b.second >= a.second)
return true;
else
return false;
}
int dfn[nmax], low[nmax], dfs_clock, color[nmax], scc;
int ss[nmax], st;
bool instack[nmax];
void tarjan(int u, const graph & g) {
dfn[u] = low[u] = ++dfs_clock;
ss[st++] = u;
instack[u] = true;
for(int i = g.head[u]; i != -1; i = g.e[i].nxt) {
int v = g.e[i].to;
if(!dfn[v]) {
tarjan(v, g);
low[u] = min(low[u], low[v]);
} else if (instack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u] == low[u]) {
scc++;
int tmp;
while(true) {
tmp = ss[--st];
color[tmp] = scc;
instack[tmp] = false;
if(tmp == u)
break;
}
}
}
bool check() {
for(int i = 1; i <= m; ++i) {
if(color[i] == color[i+m])
return false;
}
return true;
}
void init() {
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(color, 0, sizeof color);
// memset(instack, 0, sizeof instack);
scc = dfs_clock = st;
g.init();
}
int main(){
while(scanf("%d %d", &n, &m) != EOF) {
init();
int a, b;
for(int i = 1; i <= m; ++i) {
scanf("%d %d", &a, &b);
if(a < b) {
line[i].first = a;
line[i].second = b;
} else {
line[i].first = b;
line[i].second = a;
}
}
for(int i = 1; i <= m ;++i) {
for(int j = i + 1; j <= m; ++j) {
if(checkcross(line[i], line[j])) {
g.add_edge(i, j + m);
g.add_edge(i + m, j);
g.add_edge(j, i + m);
g.add_edge(j + m, i);
}
}
}
for(int i = 1; i <= 2 * m; ++i) {
if(!dfn[i])
tarjan(i, g);
}
if(check()) {
printf("panda is telling the truth...\n");
} else {
printf("the evil panda is lying again\n");
}
}
return 0;
}