ybtoj Au 2-SAT问题

前言中的前言

  1. 由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻

  2. 由于本人过菜,不知道什么时候就 AFO 了,想给这个机房留下点什么……

  3. 如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人

  4. 本篇题解是在作者极困的情况下写的(楞一下神就会睡着那种),如有错误,应评论/私信,谢谢各位

正片开始!

好耶!!!(泪目)

题面

题目传送门

前言

2-SAT T1,板子题

正文

首先明确一个事情:不选夫就相当于选妻,不选妻就相当于选夫

好的

那么我们用 a 表示选这个人,a 表示不选这个人

那么,如果两个人是仇恨关系,那么 a -> b b -> a

tarjan

如果 aa 在同一个 c 里就不行

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+6;
int n,m,head[N<<1],cnt;
struct node{int to,nxt;}e[N<<1];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],low[N],tim,st[N],top,c[N],tot;
void tarjan(int x){
	dfn[x]=low[x]=++tim;
	st[++top]=x;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(!c[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		c[x]=++tot;
		while(st[top]!=x){
			c[st[top--]]=tot;
		}
		top--;
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int p=0;
	while(cin>>n>>m){
		p++;
		cnt=top=0;
		for(int i=1;i<=N<<1-5;i++)head[i]=c[i]=dfn[i]=low[i]=0;
	for(int i=1;i<=m;i++){
		int a1,a2,c1,c2;
		cin>>a1>>a2>>c1>>c2;
		a1=(a1<<1)+c1;
		a2=(a2<<1)+c2;
		add(a1,a2^1);
		add(a2,a1^1);
	}
	for(int i=0;i<n<<1;i++){
		if(!dfn[i])tarjan(i);
	}
	bool flag=1;
	for(int i=0;i<n;i++){
		if(c[i<<1]==c[(i<<1)^1])flag=0;
	}
	if(flag)cout<<"YES"<<endl;
	else  cout<<"NO"<<endl;
	}
	return 0;
}	

后记

小学妹已经三天没来力

题面

题目传送门

前言

2-SAT T2,水题

正文

x 表示这个人与新郎同侧,x 表示与新娘同侧

那么,如果矛盾,就连 a -> b b -> a

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,head[N<<1],cnt;
struct node{int to,nxt;}e[N<<1];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],low[N],c[N],st[N],tim,top,tot;
void tarjan(int x){
	dfn[x]=low[x]=++tim;
	st[++top]=x;
	for(int i=head[x];~i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(!c[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		c[x]=++tot;
		while(st[top]!=x){
			c[st[top--]]=tot;
		}
		top--;
	}
}
bool flag;
int main(){
	while(1){
	cin>>n>>m;
	if(!n)return 0;
	flag=1;
	// for(int i=1;i<=n;i++){
	// 	add(i<<1,i<<1|1);
	// 	add(i<<1|1,i<<1);
	// }
	for(int i=0;i<=N-5;i++)dfn[i]=low[i]=c[i]=0;
	top=tot=tim=cnt=0;
	for(int i=0;i<=(N<<1)-5;i++)head[i]=-1;
	for(int i=1;i<=m;i++){
		int o,o1;char op,op1;cin>>o>>op>>o1>>op1;
		//cout<<o<<op<<o1<<op1<<endl;
		o=o*2+(op=='h'?1:0);
		o1=o1*2+(op1=='h'?1:0);
		add(o,o1^1),add(o1,o^1);
		//cout<<o<<" "<<o1<<endl;
	}
	add(0,1);
	for(int i=0;i<=n*2-1;i++){
		if(!dfn[i]){
			tarjan(i);
		}
	}
	for(int i=0;i<n;i++){
		if(c[i<<1]==c[i<<1|1]){
			flag=0;
			cout<<"bad luck"<<endl;
			break;
		}
	}
	if(flag){
		for(int i=1;i<n;i++){
			cout<<i;
			if(c[i<<1|1]>c[i<<1])cout<<"h ";
			else cout<<"w ";
		}	
		cout<<endl;
	}
	}
	return 0;
}

后记

多测!可恶的多测!!!

题面

题目传送门

前言

2-SAT T3,大水题

正文

只一个‘大’字是我对他最大的宽容

前半部分用 2-SAT 板子

关键是怎么求 NY?(怎么que我笔名)

好的

我们发现

可以 O(n2)

于是我们 dfs

然后过了

然后我们惊奇的发现

前面的 tarjan 直接用 dfs 即可……

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2e4+4;
int n,m,head[N<<1],cnt;
struct node{int to,nxt;}e[N<<1];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],low[N],c[N],st[N],top,tot,tim;
void tarjan(int x){
	dfn[x]=low[x]=++tim;
	st[++top]=x;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}else if(!c[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		c[x]=++tot;
		while(st[top]!=x){
			c[st[top--]]=tot;
		}
		top--;
	}
}
int vis[N];
void dfs(int x,int il){
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(vis[y]!=il){vis[y]=il;dfs(y,il);}
	}
}
int opp;
bool check(int x){
	vis[x]=++opp;
	//if(x==4)cout<<opp;
	dfs(x,opp);
	for(int i=1;i<=n;i++){
		if(vis[i<<1]==opp&&vis[i<<1|1]==opp)return 0;
	}
	return 1;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int o,o1;char op,op1;
		cin>>o>>op>>o1>>op1;
		o=o*2+(op=='Y');
		o1=o1*2+(op1=='Y');
		add(o^1,o1);
		add(o1^1,o);
		//cout<<(o^1)<<" "<<o1<<endl;
		//cout<<(o1^1)<<" "<<o<<endl;
	}
	for(int i=2;i<=2*n+1;i++){
		if(!dfn[i])tarjan(i);
	}
	for(int i=1;i<=n;i++){
		if(c[i<<1]==c[i<<1|1]){cout<<"IMPOSSIBLE"<<endl;return 0;}
	}
	for(int i=1;i<=n;i++){
		bool f1,f2;
		f1=check(i<<1|1);
		f2=check(i<<1);
		if(f1&&f2)cout<<"?";
		else if(f1)cout<<"Y";
		else cout<<"N";
	}
	return 0;
}

后记

无语……

题面

题目传送门

前言

2-SAT T4,数学题?

正文

首先题意是让你判断这个图是不是平面图

平面图性质:

  1. m<=3*n-6

  2. 如果环上两条线段的端点是交错的:u<x<v<y 那么需要一条在环里,一条在环外

好的

那么我们直接把 x 当作在环里 x 当作在环外

2-SAT

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2e6+6;
int t,n,m,id[N],cir[205][205],d[N],cnt1,head[N<<1],cnt;
struct Node{int x,y;}e1[N],E[N];
struct node{int to,nxt;}e[N<<1];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int dfn[N],c[N],low[N],tot,tim,top,st[N];
void tarjan(int x){
	dfn[x]=low[x]=++tim;
	st[++top]=x;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}else if(!c[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		c[x]=++tot;
		while(st[top]!=x){
			c[st[top--]]=tot;
		}
		top--;
	}
}
int main(){
	cin>>t;
	while(t--){
		tot=top=cnt=cnt1=tim=0;
		for(int i=1;i<=N-5;i++){
			dfn[i]=low[i]=c[i]=0;
		}
		for(int i=1;i<=N*2-5;i++)head[i]=0;
		memset(cir,0,sizeof(cir));	
		cin>>n>>m;
		for(int i=1;i<=m;i++){
			cin>>e1[i].x>>e1[i].y;
			if(e1[i].x>e1[i].y)swap(e1[i].x,e1[i].y);
		}
		for(int i=1;i<=n;i++){
			cin>>d[i];
			//cout<<d[i]<<" ";
			id[d[i]]=i;
			if(i>1){
				int x=d[i-1],y=d[i];
				if(x<y)cir[x][y]=1;
				else cir[y][x]=1; 
			}
		}
		if(m>3*n-6){cout<<"NO"<<endl;continue;}
		int x=d[n],y=d[1];
		if(x<y)cir[x][y]=1;
		else cir[y][x]=1;
		for(int i=1;i<=m;i++){
			if(cir[e1[i].x][e1[i].y])continue;
			E[++cnt1].x=e1[i].x,E[cnt1].y=e1[i].y;
		}
		for(int i=1;i<cnt1;i++){
			for(int j=i+1;j<=cnt1;j++){
				int u=id[E[i].x],v=id[E[i].y],x=id[E[j].x],y=id[E[j].y];
				if (u > v) swap(u, v); if (x > y) swap(x, y);
				if((u<x&&x<v&&v<y)||(x<u&&u<y&&y<v)){
					add(i,j+m),add(i+m,j),add(j,i+m),add(j+m,i);
				}
			}
		}
		for(int i=1;i<=m<<1;i++){
			if(!dfn[i])tarjan(i);
		}
		bool flag=1;
		for(int i=1;i<=m;i++){
			if(c[i]==c[i+m])flag=0;
		}
		if(flag)cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

后记

云落:要早睡早起

我的生物钟:好

然后

2:20 醒了……

题面

题目传送门

前言

2-SAT T5,应用题

正文

这里把两个中转站当作 2-SAT 是显然的

二分也是显然的

问题是怎么处理距离

总的来说

如果距离超了,就不能走,也就是在 2-SAT 上连反点

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+6;
int n,a[N],b[N],c[N],d[N],A,B,sx1,sy1,sx2,sy2,x[N],y[N],dis[N],len;
int Dis(int x,int y,int x1,int y1){
	return abs(x-x1)+abs(y-y1);
}
int tot,top,tim,dfn[N],low[N],c1[N],st[N],head[N*N*2],cnt;
struct node{int to,nxt;}e[N*N*2];
void add(int u,int v){
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void tarjan(int x){
	dfn[x]=low[x]=++tim;
	st[++top]=x;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}else if(!c1[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){ 
		c1[x]=++tot;
		while(st[top]!=x){
			c1[st[top--]]=tot;
		}
		top--;
	}
}
bool check(int mid){
	for(int i=1;i<=N-5;i++){
		dfn[i]=low[i]=c1[i]=0;
	}
	for(int i=1;i<=N*N*2-5;i++)head[i]=0;
	cnt=tot=top=tim=0;
	for(int i=1;i<=A;i++){
		add(a[i],b[i]+n),add(b[i],a[i]+n),add(a[i]+n,b[i]),add(b[i]+n,a[i]);
	}
	for(int i=1;i<=B;i++){
		add(c[i],d[i]),add(c[i]+n,d[i]+n),add(d[i],c[i]),add(d[i]+n,c[i]+n);
	}
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(dis[i]+dis[j]>mid)add(i,j+n),add(j,i+n);
			if(dis[i+n]+dis[j+n]>mid)add(i+n,j),add(j+n,i);
			if(dis[i+n]+dis[j]+len>mid)add(i+n,j+n),add(j,i);
			if(dis[i]+dis[j+n]+len>mid)add(i,j),add(j+n,i+n);
		}
	}
	for(int i=1;i<=2*n;i++){
		if(!dfn[i])tarjan(i);
	}
	for(int i=1;i<=n;i++){
		if(c1[i]==c1[i+n])return 0;
	}
	return 1;
}
int main(){
	while(cin>>n>>A>>B){
		cin>>sx1>>sy1>>sx2>>sy2;
		for(int i=1;i<=n;i++){
			cin>>x[i]>>y[i];
			dis[i]=Dis(x[i],y[i],sx1,sy1);
			dis[i+n]=Dis(x[i],y[i],sx2,sy2);
		}
		len=Dis(sx1,sy1,sx2,sy2);
		for(int i=1;i<=A;i++){
			cin>>a[i]>>b[i];
		}
		for(int i=1;i<=B;i++){
			cin>>c[i]>>d[i];
		}
		int l=0,r=8000006,ans=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(check(mid))r=mid-1,ans=mid;
			else l=mid+1;
		}
		cout<<ans<<endl;
	}
	return 0;
}

后记

玩完原神瞬间调出来力

题面

题目传送门

前言

2-SAT T6,毒瘤题

正文

题解

本蒟蒻没有调出来……

代码

#include<iostream>
#include<cstring>
#define ch getchar
#define in getint
#define ts timestamp
#define ins in_stack
using namespace std;
const int N=100010,M=200010;
int n,d,m;
char s[N];
int h[N],e[M],ne[M],idx;
int dfn[N],low[N],timestamp;
int stk[N],top;
bool in_stack[N]; 
int id[N],cnt;
int pos[10];
struct Op
{
	int x,y;
	char a,b;
}op[M];
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int in(int x,char b,int t)
{
	char a=s[x]-'a';
	b-='A';
	if(((a+1)%3!=b)^t) return x+n;
	return x;
}
char ch(int x,int t)
{
	int y=s[x]-'a';
	return 'A'+(y+3+(t? -1 :1))%3;
}
void tarjan(int u)
{
	dfn[u]=low[u]=++ts;
	stk[++top]=u,ins[u]=true;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(!dfn[j])
		{
			tarjan(j);
			low[u]=min(low[u],low[j]);
		}
		else if(ins[j])
		    low[u]=min(low[u],dfn[j]);
	}
	if(dfn[u]==low[u])
	{
		cnt++;
		int y;
		do{
			y=stk[top--];
			ins[y]=false;
			id[y]=cnt;
		} while(y!=u);
	}
}
bool launch()
{
	memset(h,-1,sizeof h);
	memset(dfn,0,sizeof dfn);
	idx=ts=cnt=0;
	for(int i=0;i<m;i++)
	{
		int x=op[i].x-1,y=op[i].y-1;
		char a=op[i].a,b=op[i].b;
		if(s[x]!=a-'A'+'a')
		{
			if(s[y]!=b-'A'+'a') add(in(x,a,0),in(y,b,0)),add(in(y,b,1),in(x,a,1));
		    else add(in(x,a,0),in(x,a,1));
		}
	}
	for(int i=0;i<n*2;i++)
	    if(!dfn[i])
	        tarjan(i);
	for(int i=0;i<n;i++)
	    if(id[i]==id[i+n])
		    return false;
	for(int i=0;i<n;i++)
	    if(id[i]<id[i+n]) printf("%c",ch(i,0));
	    else printf("%c",ch(i,1));
	return true;   
}
int main()
{
	scanf("%d%d%s",&n,&d,s);
	for(int i=0,j=0;i<n;i++)
	    if(s[i]=='x')
	        pos[j++]=i;
	scanf("%d",&m);
	for(int i=0;i<m;i++) scanf("%d %c %d %c",&op[i].x,&op[i].a,&op[i].y,&op[i].b);
	for(int k=0;k<1<<d;k++)
	{
		for(int i=0;i<d;i++)
		    if(k>>i&1) s[pos[i]]='a';
		    else s[pos[i]]='b';
		if(launch()) return 0;
	}
	puts("-1");
	return 0;
}

后记

我们,开摆!!!

posted @   小惰惰  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
/* 鼠标点击求赞文字特效 */
点击右上角即可分享
微信分享提示

目录导航