前言
最后一篇学习笔记了,之后就要咕咕咕很久了。在机房的最后一天,祝好。
原理
传送门luoguP4782
解释一下题目:
根据要求将布尔变量赋值,如1 1 3 0就是说要求将变量1为1或者变量3为0。
由于每个布尔变量只有两个值1,0。我们可以将其拆开,由此我们发现:
对于变量x,y,要求x或y要满足的值为a,b
当a=1,b=1时,如果x=0,那么一定推出y必须为1,如果y=0,同理推出x=1。
当a=1,b=0时,如果x=0,那么一定推出y必须为0,如果y=1,同理推出x=1。
当a=0,b=1时,如果x=1,那么y=1,如果y=0,那么x=0。
当a=0,b=0时,如果x=1,那么y=0,如果y=1,那么x=1。
将拆的点分为1~n表示0的情况,n+1~2n表示1的情况,通过上面的推导,我们就将这些一定能推出的连边,最后变成了一张图:
例如:
有2个变量时,要满足,
a=0,b=1;a=1,b=1;a=0,b=0,建出图就是:
我们发现x和!y在一个强连通里面,!x和y在一个强连通里面。找强连通通常用Tarjan算法,
然后我们就可以缩点,变成有向无环图DAG。
其中x和!y在强连通1中,!x和y在强连通2中,
那我们要如何赋值呢?
当x赋的1时,即在1里,此时通过x可以推出!y一定成立,!y成立,那么!x也一定成立,就相互矛盾了,当x赋为0时,仅推出!y也必须成立。
故此我们容易想到拓扑排序,我们应当要选择x和!x中拓扑序较大的点来作为值。
而Tarjan算法中dfs已经搞出了每个强连通的dfs序,
由于dfs序和拓扑序正好相反,我们只需反着来,选择x和!x在强连通编号较小的,就是拓扑序较大点来作为值。
那么如果没有解的情况呢?
如果x和!x在同一强连通里面,无论怎样赋值,x和!x总能互相推出,总会矛盾,所以这就是无解的情况,即x和!x在强连通的编号相同。
值得注意的是:
跑Tarjan时要枚举2n个点,是否dfn为0。
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int M=4e6+5;
int n,m;
bool vis[M];
int dfn[M],id[M],low[M],cnt,sum;
int first[M],nex[M],to[M],tot;
stack<int>q;
void add(int x,int y){
nex[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void dfs(int x){
dfn[x]=low[x]=++cnt;
q.push(x);vis[x]=1;
for(int i=first[x];i;i=nex[i]){
int y=to[i];
if(dfn[y]==0){
dfs(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y]){
low[x]=min(low[x],dfn[y]);
}
}
if(low[x]==dfn[x]){
sum++;
int k;
do{
k=q.top();
q.pop();
id[k]=sum;
vis[k]=0;
}while(k!=x);
}
}
int main(){
scanf("%d%d",&n,&m);
int x,y,a,b;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&x,&a,&y,&b);
if(a==0 && b==0){
add(x+n,y);
add(y+n,x);
}
if(a==1 && b==0){
add(x,y);
add(y+n,x+n);
}
if(a==0 && b==1){
add(y,x);
add(x+n,y+n);
}
if(a==1 && b==1){
add(x,y+n);
add(y,x+n);
}
}
for(int i=1;i<=2*n;i++)
if(dfn[i]==0) dfs(i);
for(int i=1;i<=n;i++)
if(id[i]==id[i+n]) {cout<<"IMPOSSIBLE";return 0;}
cout<<"POSSIBLE"<<endl;
for(int i=1;i<=n;i++)
if(id[i]>id[i+n]) cout<<1<<" ";
else cout<<0<<" ";
return 0;
}
双倍经验,传送门luoguP4171
仅需注意读入问题即可。
#include<bits/stdc++.h>
using namespace std;
const int M=4e6+5;
int n,m,t;
bool vis[M];
int dfn[M],id[M],low[M],cnt,sum;
int first[M],nex[M],to[M],tot;
stack<int>q;
void add(int x,int y){
nex[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void dfs(int x){
dfn[x]=low[x]=++cnt;
q.push(x);vis[x]=1;
for(int i=first[x];i;i=nex[i]){
int y=to[i];
if(dfn[y]==0){
dfs(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y]){
low[x]=min(low[x],dfn[y]);
}
}
if(low[x]==dfn[x]){
sum++;
int k;
do{
k=q.top();
q.pop();
id[k]=sum;
vis[k]=0;
}while(k!=x);
}
}
int main(){
scanf("%d",&t);
while(t--){
memset(vis,0,sizeof(vis));
memset(first,0,sizeof(first));
memset(dfn,0,sizeof(dfn));
tot=0;sum=0;cnt=0;
while(!q.empty()) q.pop();
scanf("%d%d",&n,&m);
char s1[20],s2[20];
for(int i=1;i<=m;i++){
int x=0,y=0,res;
cin>>s1>>s2;
res=1;while(isdigit(s1[res])) x=x*10+s1[res++]-'0';
res=1;while(isdigit(s2[res])) y=y*10+s2[res++]-'0';
int a=(s1[0]=='m')?1:0;
int b=(s2[0]=='m')?1:0;
if(a==0 && b==0){
add(x+n,y);
add(y+n,x);
}
if(a==1 && b==0){
add(x,y);
add(y+n,x+n);
}
if(a==0 && b==1){
add(y,x);
add(x+n,y+n);
}
if(a==1 && b==1){
add(x,y+n);
add(y,x+n);
}
}
for(int i=1;i<=2*n;i++)
if(dfn[i]==0) dfs(i);
int flag=0;
for(int i=1;i<=n;i++)
if(id[i]==id[i+n]){
cout<<"BAD"<<"\n";
flag=1;
break;
}
if(!flag)
cout<<"GOOD"<<"\n";
}
return 0;
}