更新
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;
}