条形座位的定义:在 $n$ 个座位中有 $2$ 个座位只有一个相邻的座位(为端点) 其余 $n - 2$ 个座位均有两个相邻的座位。($n = 1$ 时也算)


$\texttt{subtask 1-3}$

即为$n = 1$ 的情况,说明只有一行,因此一定是条形座位。那么我们只需要根据乘法原理将每组条形座位的方案相乘即可。

那么每组条形座位的方案如何计算呢?还是乘法原理!我们先回顾一下选座位的要求:任意考生不可能和来自同学校的考生座位相邻。
那么对于 $k$ 个学校的学生与一个长度为 $m$ 的条形座位。[$k \ge 2$] 第一个位置肯定有 $k$ 种情况;第二个座位的学生与第一个的来自不同的学校,所以只有 $k - 1$ 种情况;第三个座位的学生与第二个的来自不同的学校,但可以与第一个的来自相同的学校,所以仍然有 $k - 1$ 种情况。

综上所述,上面的一组条形座位的方案数为 $k \times (k - 1) ^ {n - 1}$ $[k \ge2]$。
根据这个,我们就可以得到 $40pts$ 的代码:

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
#include <iostream>
#define MOD 998244353
using namespace std;
char a[1005][1005];
int an[1005];
int main()
{
int n,m,k,w = 0;long long ans = 1;//long long!!!
cin>>n>>m>>k;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j) cin>>a[i][j];
for(int i = 1;i <= m;i++)
{
if(a[1][i] == 'O' && a[1][i - 1] != 'O') an[++w]++;//新的条形座位
else if(a[1][i] == 'O') an[w]++;//座位的长度增加
}
for(int i = 1;i <= w;i++)
{
long long num = k;
for(int j = 2;j <= an[i];j++) num *= k - 1,num %= MOD;//计算与取模
ans *= num;ans %= MOD;//答案累计取模
}
cout<<ans<<endl;
return 0;
}

$\texttt{subtask 4,5}$

我们将上面的思路由一维扩展到二维,发现我们只要进行一次搜索进行连通块数量的统计就行了。

需要多开一个 vis[1005][1005] 的数组,用于记录该点是否到达过。每一个连通块的大小都要单独计算,因此在搜索完毕后会得到若干个大小不同的条形座位,分别经过上述公式计算后相乘即可得出答案。$80pts$ 的代码如下:

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
#include <iostream>
#define MOD 998244353
#define MAX 1005
#define ll long long
using namespace std;
char a[MAX][MAX];
ll an[MAX],ans = 1;
bool vis[MAX][MAX];
int dx[4] = {1,-1,0,0},dy[4] = {0,0,1,-1},n,m,k,w;
void search(int x,int y);
int main()
{

cin>>n>>m>>k;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j) cin>>a[i][j];
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j)
if(!vis[i][j] && a[i][j] == 'O') w++,search(i,j);
for(int i = 1;i <= w;++i)//计算
{
ll sum = k;
for(int j = 2;j <= an[i];++j) sum *= k - 1,sum %= MOD;
ans *= sum,ans %= MOD;
}
cout<<ans<<endl;
return 0;
}
void search(int x,int y)
{
vis[x][y] = 1;an[w]++;//标记已经到过,答案加1
for(int i = 0;i < 4;++i)
{
int xx = x + dx[i],yy = y + dy[i];
if(1 <= xx && 1 <= yy && xx <= n && y <= m && !vis[xx][yy] && a[xx][yy] == 'O') search(xx,yy);//搜索
}
}

$\texttt{subtask 6}$

可能出现不是条形座位的情况,因此我们需要判断每个连通块是否合法。若不合法,就要特判输出 $0$ 。那么我们如何去检查呢?

我们可以开一个队列:每次搜索计数一个连通块时,搜索到一个新的点时,把该点的坐标记录到队列中去。在搜索完毕后,进行统一的检查。不断取出队列中的元素,若不满足只有 $2$ 个相邻座位数为 $1$ 的点且其余座位的相邻座位数均为 $2$,就说明不满足条件,输出 $0$ 并停止程序。需要注意的是,只有一个座位的情况需要额外的特判。

综上所述,我们就得到了 $100pts$ 的代码:

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
#include <iostream>
#include <queue>
#define MOD 998244353
#define MAX 1005
#define ll long long
using namespace std;
struct node
{
int x,y;
};
queue <node> q;
char a[MAX][MAX];
ll an[MAX],ans = 1;
bool vis[MAX][MAX];
int dx[4] = {1,-1,0,0},dy[4] = {0,0,1,-1},num[MAX][MAX],n,m,k,w;
void search(int x,int y);
bool check();
int main()
{

cin>>n>>m>>k;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j) cin>>a[i][j];
for(int i = 1;i <= n;++i)
for(int j = 1;j <= m;++j)
if(!vis[i][j] && a[i][j] == 'O')
{
w++;search(i,j);
if(!check())
{
cout<<0<<endl;
return 0;
}
}
for(int i = 1;i <= w;++i)
{
ll sum = k;
for(int j = 2;j <= an[i];++j) sum *= k - 1,sum %= MOD;
ans *= sum,ans %= MOD;
}
cout<<ans<<endl;
return 0;
}
void search(int x,int y)
{
q.push({x,y});
vis[x][y] = 1;an[w]++;
for(int i = 0;i < 4;++i)
{
int xx = x + dx[i],yy = y + dy[i];
if(1 <= xx && 1 <= yy && xx <= n && y <= m && !vis[xx][yy] && a[xx][yy] == 'O') search(xx,yy);
}
}
bool check()
{
int tt = 0;
if(q.size() == 1)
{
q.pop();
return 1;//特判
}
while(!q.empty())//检查
{
int t = 0;
node ch = q.front();q.pop();
if(a[ch.x][ch.y - 1] == 'O') t++;
if(a[ch.x][ch.y + 1] == 'O') t++;
if(a[ch.x - 1][ch.y] == 'O') t++;
if(a[ch.x + 1][ch.y] == 'O') t++;
if(t == 1) tt++;
if(t > 2) return 0;//相邻座位大于2
//cout<<ch.x<<" "<<ch.y<<" "<<t<<endl;
}
if(tt == 2) return 1;//相邻座位只有1的点恰好只有2个
else return 0;
}