luogu题解 P4171 【[JSOI2010]满汉全席】

写在前面

不会吧不会吧,这么裸的2-SAT都看不出来? 经验题

题目传送门

Solution

2-SAT处理

关于什么是2-SAT,详见Oiwiki

简单来说,2-SAT问题的形式是给一堆条件,每个条件中有两个小条件,不过只需要满足其中一种

题目中说评委的两个条件只需要满足一个即可,然后每种材料又有满式和汉式两种选择

这不显然让我们打2-SAT?可能也有其他神仙做法

而2-SAT主要考察建图这一块,我们用 \(0 \sim n\) 表示满式,用 \(n+1 \sim 2n\) 表示汉式,用 \(x^{-1}\) 表示 \(x\) 的另一种菜式

那么对于任意一个条件 \((a_i,b_i)\)

  • 首先让我们满足 \(a_i\) 而不满足 \(b_i\) ,那就从 \(a_i\)\(b_i^{-1}\) 连一条边

  • 然后让我们满足 \(b_i\) 而不满足 \(a_i\) ,那就从 \(b_i\)\(a_i^{-1}\) 连一条边

(别搞混了调半天)

字符串处理

暴力处理即可,判断第一个字符确定满式还是汉式

扫一遍字符串取出是第几号菜,然后按上面的方式加边

答案判断

用强连通分量缩点

缩点后,如果存在同一菜品出现在同一强联通分量里,那就不能满足

否则就可以满足

大体思路就这些,看代码吧

Code

/*
Work by: Suzt_ilymics
Knowledge: ??
Time: O(??)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge{
	int to, nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 0;

int T, n, m;
int dfn[MAXN], low[MAXN], cnt = 0, stc[MAXN], sc = 0;
int num[MAXN], t = 0;
bool vis[MAXN];

int read(){
	int s = 0, f = 0;
	char ch = getchar();
	while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
	while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
	return f ? -s : s;
}

void add_edge(int from, int to){ e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }

void tarjan(int u){
	stc[++sc] = u, dfn[u] = low[u] = ++cnt, vis[u] = true;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(!dfn[v]){
			tarjan(v), low[u] = min(low[u], low[v]);
		}
		else if(vis[v]) low[u] = min(low[u], dfn[v]);
	}
	if(low[u] == dfn[u]){
		int pre; ++t;
		do{
			pre = stc[sc--];
			vis[pre] = false;
			num[pre] = t;
		}while(pre != u);
	}
}

int main()
{
	T = read();
	while(T--){
		memset(head, 0, sizeof head);
		memset(vis, false, sizeof vis);
		memset(dfn, 0, sizeof dfn);
		num_edge = t = sc = cnt = 0;
		n = read(), m = read();
		char u[9], v[9];
		for(int i = 1; i <= m; ++i){
//			top = 0;
//			while(true){
//				ch = getchar();
////				cout<<ch<<endl;
//				if(ch == ' ') break;
//				else u[++top] = ch;
//			}
//			top = 0;
//			while(true){
//				ch = getchar();
//				if(ch == '\n') break;
//				else v[++top] = ch;
//				cout<<ch<<" ";
//			}
			cin >>(u + 1)>>(v + 1);
//			cout<<(u + 1)<<" "<<(v + 1)<<endl;;
			int u_ = 0, v_ = 0;
			for(int j = 2; j <= (int)strlen(u + 1); ++j) u_ = (u_ << 1) + (u_ << 3) + (int)(u[j] ^ 48);
			for(int j = 2; j <= (int)strlen(v + 1); ++j) v_ = (v_ << 1) + (v_ << 3) + (int)(v[j] ^ 48);
			if(u[1] == 'm' && v[1] == 'm') add_edge(u_, v_ + n), add_edge(v_, u_ + n);	
			if(u[1] == 'h' && v[1] == 'm') add_edge(u_ + n, v_ + n), add_edge(v_, u_);
			if(u[1] == 'm' && v[1] == 'h') add_edge(u_, v_), add_edge(v_ + n, u_ + n);
			if(u[1] == 'h' && v[1] == 'h') add_edge(u_ + n, v_), add_edge(v_ + n, u_);
		}
		for(int i = 1; i <= 2 * n; ++i){
			if(!dfn[i]) tarjan(i);
		}
		int flag = false;
		for(int i = 1;  i <= n; ++i){
			if(num[i] == num[i + n]){
				flag = true;
			}
		}
		if(flag) printf("BAD\n");
		else printf("GOOD\n");
	}
	return 0;
}

如果有什么问题,请在评论区指正

posted @ 2021-02-03 09:47  Suzt_ilymtics  阅读(54)  评论(0编辑  收藏  举报