【题解】CF1971H

一:【题意】

给定一个 \(3\)\(n\) 列的矩阵,每个位置是 \(a_i\)或者 \(-a_i\),现在问你是否能够给出一个 \(a_i\) 让每列至少两个 \(1\)
\(n<=500,1000\)组多测

二:【解法】

每列至少两个 \(1\) ,等价于 \((a\ or\ b)=(b\ or\ c)=(a\ or\ c)=1\)
\(2-SAT\) 求解

三:【代码】

#include<bits/stdc++.h>
using namespace std;
const int N=2010;
unordered_map<int,int> re;int n;
struct node{
	int x,y,z;
}q[N];
vector<int> mp[N];
void add(int x,int y){
	mp[re[-x]].push_back(re[y]);
	mp[re[-y]].push_back(re[x]);
}

int low[N],dfn[N],cnt;
vector<int> st;
int scc[N],idx;
void Tarjan(int u){
	low[u]=dfn[u]=++cnt;
	st.push_back(u);
	for(auto v:mp[u]){
		if(!dfn[v]){
			Tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(!scc[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		idx++;
		while(1){
			int x=st.back();st.pop_back();
			scc[x]=idx;
			if(x==u) break;
		}
	}
}
void SCC(){
	st.clear();
	cnt=0;idx=0;
	for(int i=-n;i<=n;i++){
		if(!i) continue;
		scc[re[i]]=dfn[re[i]]=low[re[i]]=0;
	}
	for(int i=-n;i<=n;i++){
		if(!i) continue;
		if(!dfn[re[i]]) Tarjan(re[i]);
	}
}
void solve(){
	cin>>n;
	for(int i=-n;i<=n;i++){
		if(!i) continue;
		mp[re[i]].clear();
	}
    for(int i=1;i<=n;i++) q[i]={0,0,0};
	for(int i=1;i<=3;i++){
		for(int j=1;j<=n;j++){
			int k;cin>>k;
			if(q[j].x==0) q[j].x=k;
			else if(q[j].y==0) q[j].y=k;
			else q[j].z=k;
		}
	}
	for(int i=1;i<=n;i++){
		int x=q[i].x,y=q[i].y,z=q[i].z;
		add(x,y);
		add(x,z);
		add(y,z);
	}
	SCC();
	for(int i=1;i<=n;i++){
		if(scc[re[i]]==scc[re[-i]]){
			cout<<"NO\n";
			return ;
		}
	}
	cout<<"YES\n";
}
int main(){
	int tot=0;
	for(int i=-510;i<=510;i++){
		if(i) re[i]=++tot;
	}
	int t;cin>>t;
	while(t--) solve();
	return 0;
}
posted @ 2026-01-13 10:25  Ming3398  阅读(5)  评论(0)    收藏  举报