0%

题解-LuoGu3496

这是一个非黑即白的世界。

想必各位都看过题目了,首先翻译下题意:

给一幅无向图,你可以对其每个点染色,要求白黑必须挨着,灰色必须和黑白挨着,问能否染色以及染色方案。

我们可以考虑到,灰色点必然可以被黑白交替的模式代替,所以我们只要黑白染色就好,灰色可以请出去了。怎么染色?要把每个点都要染到?还要交替染?

我们立即想到一个点的入度越少越好,因为这样一个点对于其周围点的影响最小。入度最小?如果图连通,我们可以把它降为1——$\color{red}\sf\large\text{生成树}$恰好满足这一性质。

图不连通呢?题上可是说了,如果不能成功染色,那么输出NIE退出就好,另外我们可以想到,联通的图一定是可以完成染色的,所以可以大胆输出TAK

那么!我们再回头看一眼题目,就是这样的:

给一幅无向图,找出里面的生成树,对其上的每个点黑白交替染色,问图是否连通,若连通则输出每个点的颜色(有SPJ)

是否很明了了?献上一份代码(附简注),此处我用的是常规存图+dfs染色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//在这个图中的生成树上染色就好,白黑交替染
#include <bits/stdc++.h>
#define ri register int
#define ll long long
using namespace std;
int read()
{
int num=0;
int flg=1;
char c=getchar();
while(!isdigit(c))
{
if(c=='-')
{
flg=-1;
}
c=getchar();
}
while(isdigit(c))
{
num=(num<<1)+(num<<3)+(c^48);
c=getchar();
}
return num*flg;
}
const int maxn=1e6+7;
struct edge
{
int to;
int nex;
} e[maxn];

int cnt=0;
int head[maxn];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nex=head[u];
head[u]=cnt;
}

bool col[maxn];
//非黑即白
bool vis[maxn];
//这个vis[]要用两次:
//第一次:用来排查图中是否存在孤立点
//第二次:用来记录该点是否上色
void dfs(int nd,bool cur)
//当前的点,当前的颜色(黑/白)
//灰色太尖酸了,不必参加染色。
{
vis[nd]=true;
col[nd]=cur;
for(ri i=head[nd]; i; i=e[i].nex)
{
int to=e[i].to;
if(!vis[to])
{
dfs(to,!cur);
}
}
}

int main()
{
int n=read();
int m=read();
for(ri i=1; i<=m; i++)
{
int u=read();
int v=read();
vis[u]=vis[v]=true;//不是孤立点
add(u,v);
add(v,u);
}
for(ri i=1; i<=n; i++)
{
if(!vis[i])
{
//显然,有一个点孤立,不可能全部连上。
return 0&(int)printf("NIE");
}
}
//能到这里,就说明一定可以连通.
puts("TAK");
fill(vis+1,vis+n+1,0);
//清空,准备记录是否上色
for(ri i=1; i<=n; i++)
{
if(!vis[i])
{
dfs(i,0);
}
}
for(ri i=1; i<=n; i++)
{
if(col[i]==0)
{
puts("K");
}
else
{
puts("S");
}
}
}

回顾一下:这道题我们首先排除掉了不可能存在的情况,或者说存在了亦可以被代替的情况(因为SPJ);随后我们找到了树上染色是最优的情况,解决问题。