【题解】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;
}

浙公网安备 33010602011771号