题目大意
给定一个有nnn个点mmm条边的0/10/10/1权有向图,你要给每个点赋予ABCDABCDABCD中的一个字母使得每条有向边(u,v,w)(u,v,w)(u,v,w)都满足
w=1⇐ ⇒(au,av)∈{(A,D),(A,B),(B,D),(B,A),(C,D),(C,A),(C,B)}w=1\Leftarrow\!\Rightarrow (a_u,a_v)\in\{(A,D),(A,B),(B,D),(B,A),(C,D),(C,A),(C,B)\}w=1⇐⇒(au,av)∈{(A,D),(A,B),(B,D),(B,A),(C,D),(C,A),(C,B)}
如果无解,输出NONONO;否则在第一行输出长度为nnn的由ABCDABCDABCD构成的字符串,第iii个字符表示第iii个点的权值。如果有多解,你只需要任意输出一个。
n≤105,m≤5×105,1≤x,y≤n,z∈{0,1}n\leq 10^5,m\leq 5\times 10^5,1\leq x,y\leq n,z\in\{0,1\}n≤105,m≤5×105,1≤x,y≤n,z∈{0,1},(x,y,w)(x,y,w)(x,y,w)中的x,yx,yx,y可能相同。
题解
先放一段与题目有关的材料。
ABOABOABO血型系统是血型系统的一种,把血液分为A,B,AB,OA,B,AB,OA,B,AB,O四种血型。血液由红细胞和血清等组成,红细胞表面有凝集原,血清内有凝集素。根据红细胞表面有无凝集原AAA和BBB来划分血液类型。红细胞上只有凝集原AAA的为AAA型血,其血清中有抗BBB凝集素;红细胞上只有凝集原BBB的为BBB型血,其血清中有抗AAA凝集素;红细胞上两种凝集原都有的为ABABAB型血,其血清中无凝集素;红细胞上两种凝集原皆无者为OOO型,其血清中两种凝集素皆有。有凝集原AAA的红细胞可被抗凝集素凝集;有凝集原BBB的红细胞可被抗BBB凝集素凝集。配血试验是两个人分别提供红细胞和血清并将其混合,观察是否有凝集反应。
可以发现,ABCDABCDABCD的属性分别对应A,B,AB,OA,B,AB,OA,B,AB,O型血,一条边表示一次配血试验。
设ai,bia_i,b_iai,bi分别表示第iii个人的红细胞有无凝集原A,BA,BA,B,则凝集原AAA和抗AAA凝集素的相遇条件为ax∧¬aya_x\land \lnot a_yax∧¬ay,凝集原BBB和抗BBB凝集素的相遇条件为bx∧¬byb_x\land \lnot b_ybx∧¬by。因此,每个条件为z=(ax∧¬ay)∨(bx∧¬by)z=(a_x\land \lnot a_y)\lor (b_x\land \lnot b_y)z=(ax∧¬ay)∨(bx∧¬by),那么
- 如果z=0z=0z=0,则满足¬(ax∧¬ay)∧¬(bx∧¬by)\lnot(a_x\land \lnot a_y)\land \lnot(b_x\land \lnot b_y)¬(ax∧¬ay)∧¬(bx∧¬by),即(¬ax∨ay)∧(¬bx∨by)(\lnot a_x \lor a_y)\land(\lnot b_x\lor b_y)(¬ax∨ay)∧(¬bx∨by)
- 如果z=1z=1z=1,则满足(ax∧¬ay)∨(bx∧¬by)(a_x\land \lnot a_y)\lor (b_x\land \lnot b_y)(ax∧¬ay)∨(bx∧¬by),即(ax∨bx)∧(ax∨¬by)∧(bx∨¬ay)∧(¬ay∨¬by)(a_x\lor b_x)\land(a_x\lor \lnot b_y)\land(b_x\lor \lnot a_y)\land(\lnot a_y\lor \lnot b_y)(ax∨bx)∧(ax∨¬by)∧(bx∨¬ay)∧(¬ay∨¬by)
这两种情况都是2−SAT2-SAT2−SAT的形式,对于每个iii,在2−SAT2-SAT2−SAT的图上令iii表示aia_iai,i+ni+ni+n表示¬ai\lnot a_i¬ai,i+2ni+2ni+2n表示bib_ibi,i+3ni+3ni+3n表示¬bi\lnot b_i¬bi,然后用tarjantarjantarjan即可。
时间复杂度为O(n+m)O(n+m)O(n+m)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
int n,m,ct=0,tp=0,cl=0,dfn[N*4+5],low[N*4+5],st[N*4+5],co[N*4+5];
vector<int>g[N*4+5];
void add(int xx,int yy){
g[xx].push_back(yy);
}
void tarjan(int u){
dfn[u]=low[u]=++ct;st[++tp]=u;
for(int v:g[u]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!co[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
++cl;
while(st[tp]!=u) co[st[tp--]]=cl;
co[st[tp--]]=cl;
}
}
int main()
{
// freopen("dopetobly.in","r",stdin);
// freopen("dopetobly.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
if(!z){
add(x,y);add(y+n,x+n);
add(x+2*n,y+2*n);add(y+3*n,x+3*n);
}
else{
add(x+n,x+2*n);add(x+3*n,x);
add(x+n,y+3*n);add(y+2*n,x);
add(x+3*n,y+n);add(y,x+2*n);
add(y,y+3*n);add(y+2*n,y+n);
}
}
for(int i=1;i<=n*4;i++){
if(!dfn[i]) tarjan(i);
}
for(int i=1;i<=n;i++){
if(co[i]==co[i+n]||co[i+2*n]==co[i+3*n]){
printf("NO");return 0;
}
}
printf("YES\n");
for(int i=1;i<=n;i++){
int x=co[i]<co[i+n],y=co[i+2*n]<co[i+3*n];
if(x&&!y) printf("A");
else if(!x&&y) printf("B");
else if(x&&y) printf("C");
else printf("D");
}
return 0;
}

文章描述了一种将血型配对问题转化为0/1有向图上的2-SAT问题的方法,利用tarjan算法求解,最后给出相应的权值编码。
180

被折叠的 条评论
为什么被折叠?



