1 条题解

  • 0
    @ 2025-8-24 21:46:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar cmd2001
    rm -rf ~

    搬运于2025-08-24 21:46:31,当前版本为作者最后更新于2017-11-01 20:33:07,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    其实不用2-sat的,直接并查集。

    先用平面图定理 m<=3*n+6 把边数减小到O(n)级别。

    然后设 集合 i+m 表示 不能与i共存的边 所在的集合。

    枚举每一对不能共存的边,如果已经在同一个集合内,则无解。

    否则交叉连边 fa[find(i)]=j+m , fa[find(j)]=i+m 。

    如何判断两条边是否相交?先把所有边坐标转换为哈密顿回路中的顺序坐标,对于任意两条边i,j,如果有xi<xj<yi<yj,则相交。

    注意如果两条边任意不在同一条边上的两端点相同则不可能相交。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2e3+1e1;
    
    int x[maxn<<4],y[maxn<<4],vis[maxn<<4];
    int id[maxn];
    int fa[maxn<<5];
    int T,n,m;
    
    inline int findfa(int x)
    {
        return fa[x] == x ? x : fa[x] = findfa(fa[x]);
    }
    
    inline void initfa()
    {
        for(int i=1;i<=m<<1;i++)
            fa[i] = i;
    }
    
    inline bool cross(int x1,int x2,int y1,int y2)
    {
        if( x1 == x2 || y1 == y2 || x1 == y2 || x2 == y1 )
            return 0;
        if( x1 < x2 && y1 < y2 && x2 < y1 )
            return 1;
        if( x2 < x1 && y2 < y1 && x1 < y2 )
            return 1;
        return 0;
    }
    
    
    inline bool check()
    {
        initfa();
        for(int i=1;i<=m;i++)
        {
            if( vis[i] )
                continue;
            for(int j=1;j<=m;j++)
            {
                if( vis[j] )
                    continue;
                if( !cross(x[i],x[j],y[i],y[j]) )
                    continue;
                int fai = findfa(i) , faj = findfa(j);
                if( fai == faj )
                    return 0;
                fa[fai] = findfa( j + m ),
                fa[faj] = findfa( i + m );
            }
        }
        return 1;
    }
    
    inline void init()
    {
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        memset(vis,0,sizeof(vis));
        n = m = 0;
    }
    
    inline int getint()
    {
        int ret = 0;
        char ch = getchar();
        while( ch < '0' || ch > '9' )
            ch = getchar();
        while( '0' <= ch && ch <= '9' )
            ret = ret * 10 + ( ch - '0' ),
            ch = getchar();
        return ret;
    }
    int main()
    {
        T = getint();
        
        while( T-- )
        {
            init();
            n = getint() , m = getint();
            for(int i=1;i<=m;i++)
                x[i] = getint() , y[i] = getint();
            for(int i=1;i<=n;i++)
                id[getint()] = i;
            if( m > 3 * n + 6 )
            {
                puts("NO");
                continue;
            }
            for(int i=1,a,b;i<=m;i++)
            {
                a = id[x[i]] , b = id[y[i]];
                x[i] = min( a , b ),
                y[i] = max( a , b );
            }
            for(int i=1;i<=m;i++)
                if( y[i] == x[i] + 1 || ( y[i]==n && x[i]==0) )
                    vis[i] = 1;
            if( check() )
                puts("YES");
            else puts("NO");
        }
        return 0;
    }
    
    • 1

    信息

    ID
    2282
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者