Gushing over Programming Girls|

BigSmall_En

园龄:3年2个月粉丝:3关注:5

2025-02-07 23:01阅读: 2评论: 0推荐: 0

LG3825 [NOI2017] 游戏 题解

算是真正懂 2-sat 了,难绷随机眺水题直接一发紫,不过也还好。

题目描述

小 L 计划进行 \(n\) 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

小 L 的赛车有三辆,分别用大写字母 \(A\)\(B\)\(C\) 表示。地图一共有四种,分别用小写字母 \(x\)\(a\)\(b\)\(c\) 表示。

其中,赛车 \(A\) 不适合在地图 \(a\) 上使用,赛车 \(B\) 不适合在地图 \(b\) 上使用,赛车 \(C\) 不适合在地图 \(c\) 上使用,而地图 \(x\) 则适合所有赛车参加。

适合所有赛车参加的地图并不多见,最多只会有 \(d\) 张。

\(n\) 场游戏的地图可以用一个小写字母组成的字符串描述。例如:\(S=\texttt{xaabxcbc}\) 表示小 L 计划进行 \(8\) 场游戏,其中第 \(1\) 场和第 \(5\) 场的地图类型是 \(x\),适合所有赛车,第 \(2\) 场和第 \(3\) 场的地图是 \(a\),不适合赛车 \(A\),第 \(4\) 场和第 \(7\) 场的地图是 \(b\),不适合赛车 \(B\),第 \(6\) 场和第 \(8\) 场的地图是 \(c\),不适合赛车 \(C\)

小 L 对游戏有一些特殊的要求,这些要求可以用四元组 $ (i, h_i, j, h_j) $ 来描述,共 \(m\) 个四元组,表示若在第 \(i\) 场使用型号为 \(h_i\) 的车子,则第 \(j\) 场游戏要使用型号为 \(h_j\) 的车子。

你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

如果无解,输出 -1

数据范围

\(d\leq 8,n\leq 5\times 10^4,m\leq 10^5\)

题解

呃呃呃,这个限制就 2-sat 板子,我们先假设对于四元组 \((i,h_i,j,h_j)\)\(S_i\)\(S_j\) 均不为 \(x\),即第 \(i\) 场和第 \(j\) 场均只能使用两种型号的车子,不妨假设为 \(h_i,h_i',h_j,h_j'\)

然后由于 \(x\) 的位置很少,直观上我们可以考虑枚举 \(x\) 的字母,转化为每个每场只有两种车子可以选择,即标准 2-sat 求解。又由于我们假设 \(x\) 字母为 \(a\) 时,跑一边可以同时判断这一场车子为 \(b\)\(c\) 是否有解,因此我们只需要枚举 \(x\)\(a\)\(b\) 即可,这部分对复杂度的贡献为 \(2^d\)

因为我做的时候忘记 2-sat 是啥了,所以这里就再回顾一下 2-sat 的原理和做法。

最典的 2-sat 的限制就像洛谷模板一样,条件的形式为 「\(x_i\)true / false\(x_j\)true / false」。比如 「\(x_1\) 为真或 \(x_2\) 为假」、「\(x_7\) 为假或 \(x_2\) 为假」。

这个的意思就是两个条件至少一个为真,我们以「\(x_1\) 为真或 \(x_2\) 为假」为例子。三种合法的情况为:

  • \(x_1=true,x_2=flase\)
  • \(x_1=true,x_2=true\)
  • \(x_1=flase,x_2=flase\)

因此我们可以发现:

  • \(x_1=flase\) 时,\(x_2\) 一定为 \(flase\)
  • \(x_2=true\) 时,\(x_1\) 一定为 \(true\)

那么我们把 \(x_1=flase\)\(x_1=true\) 之类的看成点,那么上面这两条关系就可以连边了:

  • \((x_1=f)\to (x_2=f)\)
  • \((x_2=t)\to (x_1=t)\)

那么如果 \((x_i=f)\)\((x_i=t)\) 在一个联通分量里面,这些条件就是互相矛盾的,否则可以找到解。

可以证明,每个 \(x_i\) 选取缩点后拓扑序较大的点可以作为一个答案。

不妨假设 \((x_i=t)\) 的拓扑序大于 \((x_i=f)\) 的拓扑序。如果他们在缩点后 DAG 上不连通,则 \(x_i\)\(t\)\(f\) 均可;如果联通,则假设 \(x_i=f\) ,最后得到 \(x_i=t\),矛盾,而 \(x_i=t\) 合法。因此选拓扑序较大的,即 \((x_i=t)\)

又因为 tarjan 缩点的时候强连通分量的编号越大,拓扑序越小,不需要额外再跑一次拓扑。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int,int>ttfa;

const int N=200005;

char s[N],t[N],f[N];
int n,m,pos[N],q;
struct ques{int u,v;char opt_u,opt_v;}lis[N];

vector<int>tar[N*3];
int dfn[N],low[N],tim,stk[N],top,tot,col[N],vis[N];
void clear(){
	tim=top=tot=0;
	for(int i=1;i<=3*n;++i){
		dfn[i]=low[i]=stk[i]=col[i]=vis[i]=0;
		tar[i].clear();
	}
}

void tarjan(int u){
	dfn[u]=low[u]=++tim;
	stk[++top]=u;vis[u]=1;
	for(auto v:tar[u]){
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(vis[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u]){
		++tot;
		do{
			col[u]=tot;
			u=stk[top--];
			vis[u]=0;
		}while(dfn[u]!=low[u]);
	}
}

bool calc(){
	clear();
	for(int i=1;i<=n;++i){
		if(s[i]=='a')t[i]='b',f[i]='c';
		if(s[i]=='b')t[i]='a',f[i]='c';
		if(s[i]=='c')t[i]='a',f[i]='b';
	}
	for(int i=1;i<=q;++i){
		int tu=0,fu=0,tv=0,fv=0;
		tu=n*(t[lis[i].u]-'a')+lis[i].u;
		fu=n*(f[lis[i].u]-'a')+lis[i].u;
		if(f[lis[i].u]==lis[i].opt_u)swap(tu,fu);
		tv=n*(t[lis[i].v]-'a')+lis[i].v;
		fv=n*(f[lis[i].v]-'a')+lis[i].v;
		if(f[lis[i].v]==lis[i].opt_v)swap(tv,fv);

		if(s[lis[i].u]==lis[i].opt_u)continue;
		if(s[lis[i].v]==lis[i].opt_v){
			tar[tu].push_back(fu);
		}else{
			tar[tu].push_back(tv);
			tar[fv].push_back(fu);
		}
	}
	for(int i=1;i<=3*n;++i)if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;++i){
		int tu=n*(t[i]-'a')+i,fu=n*(f[i]-'a')+i;
		if(col[tu]==col[fu])return 0;
	}
	return 1;
}

int main(){
	scanf("%d%d",&n,&m);m=0;
	scanf("%s",s+1);
	for(int i=1;i<=n;++i){
		if(s[i]=='x')pos[++m]=i;
	}
	scanf("%d",&q);
	for(int i=1;i<=q;++i){
		scanf("%d %c %d %c",&lis[i].u,&lis[i].opt_u,&lis[i].v,&lis[i].opt_v);
		lis[i].opt_u+=32;lis[i].opt_v+=32;
	}
	for(int S=0;S<(1<<m);++S){
		for(int i=1;i<=m;++i){
			if((S>>(i-1))&1){
				s[pos[i]]='b';
			}else s[pos[i]]='a';
		}
		if(!calc())continue;
		for(int i=1;i<=n;++i){
			int tu=n*(t[i]-'a')+i,fu=n*(f[i]-'a')+i;
			if(col[tu]<col[fu])putchar(t[i]-'a'+'A');
			else putchar(f[i]-'a'+'A');
		}puts("");
		return 0;
	}
	puts("-1");
	return 0;
}

本文作者:BigSmall_En

本文链接:https://www.cnblogs.com/BigSmall-En/p/18703455

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BigSmall_En  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起