Shopee Code League 2022 - Qualification Round P3.Connecting The Numbers(线段树/二分图判定, to be discussed)

更新

2023年7月5日更新:

洛谷P3209原题,但是加强了下数据范围

tarjan老爷子发明的线性平面图判定,或许能解决这个问题

【线性平面图判定算法】_hopcroft-tarjan__beginend的博客-CSDN博客

以下是原答案,自己之前的头脑风暴

题目

t(t<=50)组样例,每次给定一个长度为2*n(n<=1e5)的序列,且每个1到n的数恰好出现两次,

圆环上等距离分布着1到2*n这2*n个点,数值相同的点要么在环内连边,要么在环外连边,

问给定的序列是否可以连边,使得边两两不交,如果可以输出yes,否则输出no

自己的一点想法

官方给出的题解,已经被找出了反例

1 
4 
1 2 3 2 4 3 1 4

队友请教了一下@泛白老哥,得出了一个主席树的做法

 自己又仔细想了想,感觉这个主席树,好像不太有必要

所以,根据这个主席树,手撸了一个线段树的做法

思路

每个数值对应的区间处理成[li,ri]的形式,按l排增序,

如果把相交线段认为是两个相邻的点,不妨涂黑认为从外连,涂白认为从内连,

则问题等价于如果相交线段两两连边,最后的图是一个二分图,暴力check是O(n^2)的

所以,考虑如果[li,ri]和[lj,rj]相交(i<j),即li<lj<ri<rj的情况,只令j->i连一条边

即每条线段i,只用考虑,与i相交且右端点在ri左侧的线段j

先正着扫一遍,把所有位置插到线段树上,

线段i的贡献放在ri的位置,这样只有包含ri的线段才能和i相交

再倒着删一遍,对于每条线段涂色,

1.如果该线段没有颜色或已经白色,检查其相邻所有点是否均为没有颜色或已经黑色,

如是,则将其相邻所有点染成黑色,将当前点染成白色

2.如果该线段没有颜色或已经黑色,检查其相邻所有点是否均为没有颜色或已经白色,

如是,则将其相邻所有点染成白色,将当前点染成黑色

3.如上述两种情况均不满足,无解

4.删掉当前节点,防止因为线段包含情况给后续带来影响,

因为后续是要查整个区间的,如区间[2,3]、[1,4],

查[1,4]的时候可能查到[2,3],但二者是包含关系,没有边的关系限制

写在最后

目前只是和二分图判定跑了跑对拍,数据是随机随的,会比较弱

有同学找到了一个弱版本的原题poj3207,改了一下输入输出交了一发过了

但还是有点不确定其正确性,所以如果有网友发现代码有bug或者直接做法假了,欢迎直接留言评论

线段树代码O(nlogn)

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=1e5+10;
const int NOCOLOR=0,BLACK=1,WHITE=2;
//线段树判断二分图
//segment biparpite grpah judge
struct segtree3{
	int n;
	struct node{int l,r,v,b,w,c;}e[N<<2];
	#define l(p) e[p].l
	#define r(p) e[p].r
	#define v(p) e[p].v // black+white
    #define b(p) e[p].b // black
    #define w(p) e[p].w // white
    #define c(p) e[p].c // 覆盖标记 1表示黑色 2表示白色 coverFlag while 1 means black and 2 means white 
	void up(int p){
        v(p)=v(p<<1)+v(p<<1|1);
        b(p)=b(p<<1)+b(p<<1|1);
        w(p)=w(p<<1)+w(p<<1|1);
    }
	void bld(int p,int l,int r){
		l(p)=l;r(p)=r;c(p)=NOCOLOR;
		if(l==r){v(p)=NOCOLOR;b(p)=w(p)=0;return;}
		int mid=l+r>>1;
		bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
		up(p);
	}
	void init(int _n){n=_n;bld(1,1,n);}
    void psd(int p){
        if(!v(p))return;
        if(c(p)==BLACK){//black
            c(p<<1)=c(p);
            c(p<<1|1)=c(p);
            b(p<<1)=v(p<<1);
            b(p<<1|1)=v(p<<1|1);
            w(p<<1)=0;
            w(p<<1|1)=0;
            c(p)=0;
        }
        else if(c(p)==WHITE){//white
            c(p<<1)=c(p);
            c(p<<1|1)=c(p);
            w(p<<1)=v(p<<1);
            w(p<<1|1)=v(p<<1|1);
            b(p<<1)=0;
            b(p<<1|1)=0;
            c(p)=0;
        }
    }
    void addPoint(int p,int x){
        if(l(p)==r(p)){
            v(p)++;
            return;
        }
		int mid=l(p)+r(p)>>1;
        psd(p);
		addPoint(p<<1|(x>mid),x);
        up(p);
    }
    void deletePoint(int p,int x){
		if(l(p)==r(p)){
            if(b(p))b(p)--;
            else if(w(p))w(p)--;
            v(p)--;
            return;
        }
		int mid=l(p)+r(p)>>1;
        psd(p);
        deletePoint(p<<1|(x>mid),x);
        up(p);
	}
    int getColor(int p,int x){
        if(l(p)==r(p)){
            if(!b(p) && !w(p))return NOCOLOR;
            if(b(p))return BLACK;
            if(w(p))return WHITE;
        }
		int mid=l(p)+r(p)>>1;
        psd(p);
		return getColor(p<<1|(x>mid),x);
    }
    void covBlack(int p,int ql,int qr){
        //if(!v(p))return;
		if(ql<=l(p)&&r(p)<=qr){
            b(p)=v(p);
			c(p)=BLACK;
			return;
		}
		psd(p);
		int mid=l(p)+r(p)>>1;
		if(ql<=mid)covBlack(p<<1,ql,qr);
		if(qr>mid)covBlack(p<<1|1,ql,qr);
		up(p);
	} 
    void covWhite(int p,int ql,int qr){
        if(!v(p))return;
		if(ql<=l(p)&&r(p)<=qr){
            w(p)=v(p);
			c(p)=WHITE;
			return;
		}
		psd(p);
		int mid=l(p)+r(p)>>1;
		if(ql<=mid)covWhite(p<<1,ql,qr);
		if(qr>mid)covWhite(p<<1|1,ql,qr);
		up(p);
	}
	bool allBlack(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr){
            return w(p)==0;
        }
		int mid=l(p)+r(p)>>1;
        psd(p);
		if(ql<=mid && !allBlack(p<<1,ql,qr))return 0;
		if(qr>mid && !allBlack(p<<1|1,ql,qr))return 0;
		return 1;
	}
    bool allWhite(int p,int ql,int qr){
		if(ql<=l(p)&&r(p)<=qr){
            return b(p)==0;
        }
		int mid=l(p)+r(p)>>1;
        psd(p);
		if(ql<=mid && !allWhite(p<<1,ql,qr))return 0;
		if(qr>mid && !allWhite(p<<1|1,ql,qr))return 0;
		return 1;
	}
}seg;
int T,n,v,pos[M];
struct node{
    int l,r;
    node(){}
    node(int a,int b){l=a;r=b;}
}e[M];
bool operator<(node a,node b){
    return a.l<b.l;
}
int main(){
    //freopen("data.txt","r",stdin);
    //freopen("segmentTree.txt","w",stdout);
    scanf("%d",&T);
    while(T--){
        memset(pos,0,sizeof pos);
        scanf("%d",&n);
        int c=0;
        for(int i=1;i<=2*n;++i){
            scanf("%d",&v);
            if(!pos[v])pos[v]=i;
            else e[++c]=node(pos[v],i);
        }
        sort(e+1,e+n+1);
        seg.init(2*n);
        for(int i=1;i<=n;++i){
            seg.addPoint(1,e[i].r);
        }
        bool ok=1;
        for(int i=n;i>=1;--i){
            int col=seg.getColor(1,e[i].r);
            if(col!=BLACK && seg.allBlack(1,e[i].l,e[i].r-1)){
                // 如果当前点非黑,且相邻点黑色或无色,给当前点染白色,相邻点全染黑色
                // if current node is not black and adjacant node is black(or no color), color current white and adjacant black
                // seg.addWhite(1,e[i].r); 没有必要实际染,与i相邻的边都在此刻被检查
                // we needn't color current code actually cause after considering these edges, current node is isolated.
                seg.covBlack(1,e[i].l,e[i].r-1);
            }
            else if(col!=WHITE && seg.allWhite(1,e[i].l,e[i].r-1)){
                // 如果当前点非白,且相邻点白色或无色,给当前点染黑色,相邻点全染白色
                // if current node is not white and adjacant node is white(or no color), color current black and adjacant white
                //seg.addBlack(1,e[i].r); 没有必要实际染,与i相邻的边都在此刻被检查
                // we needn't color current code actually cause after considering these edges, current node is isolated.
                seg.covWhite(1,e[i].l,e[i].r-1);
            }
            else{// 没解 no solution
                ok=0;
                break;
            }
            // 与i相交的区间在这一刻都被检查完了,防止对包含的情况产生影响(如[2,3]、[1,4],此时应该删掉[2,3])
            // Prevent the influence on the situation where A segment is included in B segment. When [2,3] contains in [1,4], we should delete [2,3] here
            seg.deletePoint(1,e[i].r);
        }
        puts(ok?"yes":"no");
    }
    return 0;
}

二分图代码O(n^2)

#include <bits/stdc++.h>
using namespace std;
const int M=1e5+10;
int T,n,v,pos[M],col[M];
struct node{
    int l,r;
    node(){}
    node(int a,int b){l=a;r=b;}
}e[M];
vector<int>f[M];
bool dfs(int u,int c){
    col[u]=c;
    for(auto &v:f[u]){
        if(col[v]==c)return 0;
        if(!col[v] && !dfs(v,-c))return 0;
    }
    return 1;
}
int main(){
    //freopen("data.txt","r",stdin);
    //freopen("biPartiteGragh.txt","w",stdout);
    scanf("%d",&T);
    while(T--){
        memset(pos,0,sizeof pos);
        memset(col,0,sizeof col);
        scanf("%d",&n);
        int c=0;
        for(int i=1;i<=2*n;++i){
            scanf("%d",&v);
            if(!pos[v])pos[v]=i;
            else e[++c]=node(pos[v],i);
        }
        for(int i=1;i<=n;++i){
            f[i].clear();
        }
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                if(e[i].l<e[j].l && e[j].r<e[i].r)continue;//包含 contain
                if(e[j].l<e[i].l && e[i].r<e[j].r)continue;//包含 contain
                if(max(e[i].l,e[j].l)<min(e[i].r,e[j].r)){//相交 intersect
                    //printf("i:%d (%d,%d) j:%d (%d,%d)\n",i,e[i].l,e[i].r,j,e[j].l,e[j].r);
                    f[i].push_back(j);
                    f[j].push_back(i);
                }
            }
        }
        bool ok=1;
        for(int i=1;i<=n;++i){
            if(!col[i]){
                ok&=dfs(i,1);
                if(!ok)break;
            }
        }
        puts(ok?"yes":"no");
    }
    return 0;
}

生成数据代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define random(l,r) (rand()%(r-l)+l)
int t,n,a[N];
int main(){
    srand(time(0));
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    t=10;
    freopen("data.txt","w",stdout);
    printf("%d\n",t);
    while(t--){
        n=random(5,10);
        for(int i=1;i<=n;++i){
            a[2*i-1]=a[2*i]=i;
        }
        random_shuffle(a+1,a+2*n+1);
        printf("%d\n",n);
        for(int i=1;i<=2*n;++i){
            printf("%d%c",a[i]," \n"[i==2*n]);
        }
    }
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值