bzoj1997[Hnoi2010]Planar 2-SAT

本文探讨了解决圆上两条边相交问题的方法,利用2-SAT算法,并结合平面图理论进行剪枝优化,避免算法复杂度过高。文中还介绍了使用并查集解决该问题的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟poj3207的思想差不多,就是圆上的两条边如果相交,一定得有一条在圆外,然后就是2-SAT的模型了。
要注意一下直接上2-SAT会爆炸,加一个剪枝,就是平面图要求边数<=3n-6
好像还可以用并查集做。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
const int inf=1e9;
int n,m,ind,top,cnt,scc;
int x[N],y[N];
int c[N],pos[N];
int head[N],next[N*10],go[N*10],dfn[N],low[N],bl[N],q[N];
bool vis[N];
int tot;
inline void add(int x,int y)
{
    go[++tot]=y;
    next[tot]=head[x];
    head[x]=tot;
} 
inline void tarjan(int x)
{
    vis[x]=1,q[++top]=x;
    low[x]=dfn[x]=++ind;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (!dfn[v])tarjan(v),low[x]=min(low[x],low[v]);
        else if (vis[v])low[x]=min(low[x],dfn[v]);
    }
    if (low[x]==dfn[x])
    {
        scc++;
        int t=-1;
        while (t!=x)
        {
            t=q[top--];
            vis[t]=0;
            bl[t]=scc;
        }
    }
}
inline bool pd()
{
    fo(i,1,m)
    if (bl[2*i]==bl[2*i-1])return 0;
    return 1;
}
int main()
{
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d%d",&n,&m);
        fo(i,1,m)scanf("%d%d",&x[i],&y[i]);
        tot=0,memset(head,0,sizeof(head));
        scc=ind=0;
        memset(low,0,sizeof(low));
        memset(dfn,0,sizeof(dfn));
        fo(i,1,n)scanf("%d",&c[i]),pos[c[i]]=i;
        if (m>3*n-6)
        {
            puts("NO");
            continue;
        }
        top=0;
        fo(i,1,m)
        {
            y[i]=pos[y[i]],x[i]=pos[x[i]];
            if (x[i]>y[i])swap(x[i],y[i]);
            if (y[i]-x[i]==1||(y[i]==n&&x[i]==1))continue;
            x[++top]=x[i],y[top]=y[i];
        }
        m=top;
        fo(i,1,m)
        fo(j,i+1,m)
        if ((x[i]<x[j]&&x[j]<y[i]&&y[i]<y[j])||(x[j]<x[i]&&x[i]<y[j]&&y[j]<y[i]))
        {
            add(2*i-1,2*j);
            add(2*i,2*j-1);
            add(2*j-1,2*i);
            add(2*j,2*i-1);
        }
        top=0;
        fo(i,1,2*m)if (!dfn[i])tarjan(i);
        if (pd())puts("YES");
        else printf("NO\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值