qwq这个东西我询问了几位神仙
发现后面构造方案的时候只能感性理解
首先 是用来求解类似 每个物品有一个属性,真或假,要求满足 个形如二者同时存在,都不存在,只能存在一个之类的条件。
那么这时候就需要 了
这个算法的实现过程大概是
我们对于每一个点建立两个点 和 表示这个点是真或者是假。
然后对于题目中给出的关系进行建边,一条 的边表示如果我们选了 这个状态,那么就必须选择 对应的状态。
qwq以这个题为栗子。
因为二者是至少存在一个的关系
假设前者的1必须和后者的0至少存在一个
那么相当于如果选择前者的0,后者必须选择0,如果选择后者的1,前者必须也选1。
我们就将前者0对应的节点连着后者的0的节点,后面同理。
之后呢,我们会得到一个有向图,进行 缩点之后呢,我们就可以进行可行性的 了
不难发现,如果同一个点的两个状态在同一个强连通分量里面的话,一定是不合法的,因为我们没有办法同时选择他们。
qwq貌似如果在上述的情况,就一定存在合法情况了
但是我们应该怎么求这样一个方案呢
其实这里也是比较的感性
因为一个点的出边表示他的限制数。所以我们对于一个点,应该尽量选限制少的,那么应该这么做呢?
考虑建反图,然后出边变成入边,实际上就是求反图拓扑序在前面的。
也就是反图拓扑序比较小的。
而反图的拓扑序等于原图的
编号
所以直接取小的那个就行
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e6+1e2;
const int maxm = 2*maxn;
int point[maxn],nxt[maxm],to[maxm];
int cnt,n,m;
int vis[maxn],dfn[maxn],low[maxn];
int roo[maxn];
int bel[maxn],scc;
int s[maxn];
int top;
//对于构造方案,我们之所以根据真假两个点所在的scc来决定,是因为scc的编号相当于反图的拓扑序,之所以要求反图的拓扑序选小的。
//这里我询问了几位爷 发现这个东西只能够感性理解。因为反图的拓扑序小,相当于限制少,那么选他构成合法方案的可能性就会更大,就能避开无解的情况
void addedge(int x,int y)
{
///cout<<x<<" "<<y<<endl;
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
}
int tot;
void tarjan(int x)
{
// cout<<x<<" "<<dfn[x]<<endl;
dfn[x]=low[x]=++tot;
vis[x]=1;
s[++top]=x;
// cout<<x<<endl;
for (int i=point[x];i;i=nxt[i])
{
//cout<<1<<endl;
int p = to[i];
if (!dfn[p])
{
tarjan(p);
low[x]=min(low[x],low[p]);
}
else
if (vis[p])
low[x]=min(low[x],dfn[p]);
}
// cout<<1<<endl;
if (low[x]==dfn[x])
{
scc++;
while(s[top+1]!=x)
{
//cout<<top<<" "<<s[top+1]<<" "<<x<<endl;
bel[s[top]]=scc;
roo[s[top]]=x;
vis[s[top]]=0;
top--;
}
}
}
int main()
{
n=read();m=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read(),w=read();
addedge(x+y*n,z+(w^1)*n);
addedge(z+w*n,x+(y^1)*n);
}
for (int i=1;i<=2*n;i++)
{
if (!dfn[i]) tarjan(i);
}
for (int i=1;i<=n;i++)
{
if (bel[i] == bel[i+n])
{
cout<<"IMPOSSIBLE\n"<<"\n";
return 0;
}
}
cout<<"POSSIBLE"<<"\n";
for (int i=1;i<=n;i++)
{
if(bel[i]<bel[i+n]) cout<<"1 ";
else cout<<"0 ";
}
return 0;
}
整理一下建图的方法。
模型一:两者(A,B)不能同时取
那么选择了A就只能选择B’,选择了B就只能选择A’
连边A→B’,B→A’
模型二:两者(A,B)不能同时不取
那么选择了A’就只能选择B,选择了B’就只能选择A
连边A’→B,B’→A
模型三:两者(A,B)要么都取,要么都不取
那么选择了A,就只能选择B,选择了B就只能选择A,选择了A’就只能选择B’,选择了B’就只能选择A’
连边A→B,B→A,A’→B’,B’→A’
模型四:两者(A,A’)必取A
那么,那么,该怎么说呢?先说连边吧。
连边A’→A
搬了一位巨爷的博客。
orzzzzzzzzz