[NOI2017]游戏 题解

本文深入探讨了2-SAT(二元可满足性)问题的解决策略,通过将111种不适合的条件转化为222种适合的条件,利用图论中的强连通分量概念,提出了一种高效解决方案。文章详细介绍了如何构建图模型,应用Tarjan算法进行强连通分量的检测,以及如何处理特殊情况,为理解复杂逻辑问题提供了一种新颖视角。

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

考虑把有111种车不适合跑这样的条件转化一下
111种不适合跑⟶\longrightarrow222种适合跑,并且只能选一种车去跑
可以看到这就是一个2−SAT2-SAT2SAT模板了。
考虑一下限制。
对于每场比赛拆成222个点,xxx以及x′x'x,表示这场比赛使用了哪辆适合的车
iii图选择hih_ihijjj图选择hjh_jhj
如果hih_ihi不适合在iii用,直接略过。
反之,如果hjh_jhj并不适合在jjj用,那么就让iiii′i'i连边,表示这个方案并不可行。
如果都适合则连边i,ji,ji,ji′,j′i',j'i,j
最后检测iiii′i'i是否在一个强联通分量里即可。
但是有一种图满足333种车都可以跑,考虑到这种图非常少(≤8\leq 88 ),暴力枚举它适合哪两辆车即可。判定所有方案是否存在可行解即可。

#include <bits/stdc++.h>
int n,d;
int m;
int Time,top;
char s[201000];
int cn[201000];
int dfn[201000];
int low[201000];
int tck[201000];
int vis[201000];
int pos[10],C;
int scc[201000],c;
int head[201000],tot;
struct qwq{
	int p1;
	char qwp1[1];
	int use1;
	int p2;
	char qwp2[1];
	int use2;
}limit[201000];
struct edge{
	int to;
	int nxt;
}e[201000];
void add(int x,int y){
	e[++tot]={y,head[x]};
	head[x]=tot;
}
std::pair<int,int> point(int place,int use){
	if(cn[place]==1)
	  return use==2?std::make_pair(place,place+n):std::make_pair(place+n,place);
	else
	  return use==1?std::make_pair(place,place+n):std::make_pair(place+n,place);
}
void make_edge(){
  for(int i=1;i<=m;++i){
  	if(cn[limit[i].p1]==limit[i].use1)
  	  continue;
  	std::pair<int,int> fst=point(limit[i].p1,limit[i].use1);
  	if(cn[limit[i].p2]==limit[i].use2){
  	  add(fst.first,fst.second);
  	  continue;
  	}
    std::pair<int,int> scd=point(limit[i].p2,limit[i].use2);
    add(fst.first,scd.first);
    add(scd.second,fst.second);
  }
}
void tarjan(int x){
	dfn[x]=low[x]=++Time;
	tck[++top]=x;
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=std::min(low[x],low[y]);
		}
		else
		  if(vis[y])
		    low[x]=std::min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]){
		++c;
		while(tck[top]!=x){
			vis[tck[top]]=0;
			scc[tck[top]]=c;
			--top;
		}
		vis[tck[top]]=0;
		scc[tck[top]]=c;
		--top;
	}
}
main(){
	scanf("%d%d",&n,&d);
	scanf("%s",s+1);
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
	  scanf("%d%s%d%s",&limit[i].p1,limit[i].qwp1,&limit[i].p2,limit[i].qwp2);
	  limit[i].use1=limit[i].qwp1[0]-'A'+1;
	  limit[i].use2=limit[i].qwp2[0]-'A'+1;
	}
	int len=strlen(s+1);
	for(int i=1;i<=len;++i){
		if(s[i]=='a')
		  cn[i]=1;
		if(s[i]=='b')
		  cn[i]=2;
		if(s[i]=='c')
		  cn[i]=3;
		if(s[i]=='x')
		  pos[++C]=i;
	}
	for(int i=0;i<=(1<<d)-1;++i){
		tot=c=Time=top=0;
		memset(head,0,sizeof head);
	  memset(dfn,0,sizeof dfn);
	  memset(low,0,sizeof low);
	  memset(scc,0,sizeof scc);
	  memset(tck,0,sizeof tck);
	  memset(vis,0,sizeof vis);
	  for(int j=0;j<d;++j)
	    if(i&(1<<j))
	      cn[pos[j+1]]=1;
	    else
	      cn[pos[j+1]]=2;
	  make_edge();
	  for(int j=1;j<=2*n;++j)
	    if(!dfn[j])
	      tarjan(j);
	  int f=0;
	  for(int j=1;j<=n;++j)
	  	if(scc[j]==scc[j+n])
	      f=1;
	  if(!f){
	  	for(int j=1;j<=n;++j){
	  	  if(cn[j]==1)
	  	    if(scc[j]<scc[j+n])
	  	      putchar('B');
	  	    else
	  	      putchar('C');
	  	  if(cn[j]==2)
	  	    if(scc[j]<scc[j+n])
	  	      putchar('A');
	  	    else
	  	      putchar('C');
	  	  if(cn[j]==3)
	  	    if(scc[j]<scc[j+n])
	  	      putchar('A');
	  	    else
	  	      putchar('B');
	  	}
	  	return 0;
	  }
	}
	puts("-1");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值