[题解]POI 0106 Peaceful Commission 和平委员会
【题目描述】 [Special Judge]
根据宪法,Byteland民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。此委员会必须满足下列条件:
(1)每个党派都在委员会中恰有1个代表,
(2)如果2个代表彼此厌恶,则他们不能都属于委员会。
每个党在议会中有2个代表。代表从1编号到2n。 编号为2i-1和2i的代表属于第i个党派。
任务:
写一程序,从文本文件peace.in读入党派的数量和关系不友好的代表对, 计算决定建立和平委员会是否可能,若行,则列出委员会的成员表, 结果写入文本文件peace.out。
【输入】
第一个行有2非负整数n和m。 他们各自表示:党派的数量n,1 < =n < =8000和不友好的代表对m,0 <=m <=20000。 在下面m行的每行为一对整数a,b,1<=a <b<=2n,中间用单个空格隔开。 它们表示代表a,b互相厌恶。
【输出】
如果委员会不能创立,文本文件peace.out中应该包括单词NIE。若能够成立,文本文件peace.out中应该包括n个从区间1到2n选出的整数,按升序写出,每行一个,这些数字为委员会中代表的编号。如果委员会能以多种方法形成,程序可以只写他们的某一个。
【样例】
peace.in
3 2
1 3
2 4
peace.out
1
4
5
-----------------------------------------------------------------------------------------------------------------------------------
【题解】
一道很裸的2-SAT问题。
考虑建图:我们规定i和_i为同一个党派的两个委员。若i与j有矛盾,则连边(i,_j)和(j,_i)。然后做一次Tarjan找强连通分量,缩图。判断缩图后的每个连通块是否合法,即看i和_i是否在同一个块中。然后对新图进行拓扑排序,自底向上进行选择、删除,最后得解。具体实现参见代码:
1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 using namespace std;
5 #define MAXN 8020
6 #define MAXM 20020
7 int n,m;
8 struct node
9 {
10 int v;
11 node *next;
12 };
13 node edge[MAXM*2];
14 node *cnt=&edge[0];
15 node *adj[MAXN];
16 node edge2[MAXM*2];
17 node *cnt2=&edge2[0];
18 node *adj2[MAXN];
19 int dfn[MAXN],low[MAXN],dcnt;
20 int stack[MAXN],top;
21 int Belong[MAXN],Num[MAXN],opp[MAXN],scc;
22 int In[MAXN],q[MAXN],col[MAXN];
23 bool Instack[MAXN],ans[MAXN];
24 inline void Get_int(int &Ret)
25 {
26 char ch;
27 bool flag=false;
28 for(;ch=getchar(),ch<'0'||ch>'9';)
29 if(ch=='-')
30 flag=true;
31 for(Ret=ch-'0';ch=getchar(),ch>='0'&&ch<='9';Ret=Ret*10+ch-'0');
32 flag&&(Ret=-Ret);
33 }
34 inline int Get(int x)
35 {
36 if(x%2)
37 return x+1;
38 return x-1;
39 }
40 inline void Addedge(int u,int v)
41 {
42 node *p=++cnt;
43 p->v=v;
44 p->next=adj[u];
45 adj[u]=p;
46 }
47 inline void Addedge2(int u,int v)
48 {
49 node *p=++cnt2;
50 p->v=v;
51 p->next=adj2[u];
52 adj2[u]=p;
53 }
54 void Read()
55 {
56 Get_int(n);
57 n*=2;
58 Get_int(m);
59 int i,j,k;
60 for(i=1;i<=m;i++)
61 {
62 Get_int(j);
63 Get_int(k);
64 Addedge(j,Get(k));
65 Addedge(k,Get(j));
66 }
67 }
68 void Tarjan(int u)
69 {
70 int v;
71 dfn[u]=low[u]=++dcnt;
72 stack[++top]=u;
73 Instack[u]=true;
74 for(node *p=adj[u];p;p=p->next)
75 {
76 v=p->v;
77 if(!dfn[v])
78 {
79 Tarjan(v);
80 low[u]=min(low[u],low[v]);
81 }
82 else if(Instack[v])
83 low[u]=min(low[u],dfn[v]);
84 }
85 if(dfn[u]==low[u])
86 {
87 scc++;
88 do
89 {
90 v=stack[top];
91 top--;
92 Instack[v]=false;
93 Belong[v]=scc;
94 Num[scc]++;
95 }while(v!=u);
96 }
97 }
98 bool Work()
99 {
100 int i;
101 for(i=1;i<=n;i++)
102 if(!dfn[i])
103 Tarjan(i);
104 for(i=1;i<=n;i+=2)
105 {
106 if(Belong[i]==Belong[i+1])
107 return false;
108 opp[Belong[i]]=Belong[i+1];
109 opp[Belong[i+1]]=Belong[i];
110 }
111 int u,v;
112 for(i=1;i<=n;i++)
113 for(node *p=adj[i];p;p=p->next)
114 {
115 v=p->v;
116 if(Belong[i]!=Belong[v])
117 {
118 Addedge2(Belong[v],Belong[i]);
119 In[Belong[i]]++;
120 }
121 }
122 int l=0,r=0;
123 for(i=1;i<=scc;i++)
124 if(!In[i])
125 {
126 q[r]=i;
127 r++;
128 }
129 while(l<r)
130 {
131 u=q[l];
132 l++;
133 if(!col[u])
134 {
135 col[u]=1;
136 col[opp[u]]=-1;
137 }
138 for(node *p=adj2[u];p;p=p->next)
139 {
140 v=p->v;
141 In[v]--;
142 if(!In[v])
143 {
144 q[r]=v;
145 r++;
146 }
147 }
148 }
149 for(i=1;i<=n;i+=2)
150 if(col[Belong[i]]==1)
151 ans[i]=true;
152 return true;
153 }
154 void Print()
155 {
156 if(Work())
157 {
158 int i;
159 for(i=1;i<=n;i+=2)
160 if(ans[i])
161 printf("%d\n",i);
162 else
163 printf("%d\n",i+1);
164 }
165 else
166 printf("NIE\n");
167 }
168 int main()
169 {
170 Read();
171 Print();
172 return 0;
173 }
代码没有经过缩减,所以比较冗长,但自以为写得比较清楚。望指导^w^