0%

[QOJ5442] Referee Without Red

好牛的计数题。

题目链接

题意

给定一个 的矩阵,第 行第 列的元素是 ,不同位置可能有相同的元素。

现在可以进行任意次操作,若选择对第 行操作,会先把所有第 行所有偶数列的元素按顺序拿出来,然后再按顺序放在第 行最后面。

对第 列操作类似。问最后能换出多少种不同的矩阵。

题解

这题的具体操作其实不影响做法,不妨将对行操作看为,将整行乘上一个输入的置换;对列操作相同。

容易发现,我们重新排布一下各个行的位置(与输入的置换),是不影响答案的。

那我们不妨,把同一个置换环中的行,连续排布在一起;对列进行相同的操作。

这样就可以认为,我们把行与列都连续的分为了若干组,一次对行的操作,相当于是行内处于同一组的元素,做一个循环位移。

首先考虑,若所有行分成的组中,有一组大小为 的情况。

这个时候无论对哪一列进行操作,都不会影响到这一行的元素,所以这部分相对于外面是独立的,贡献可以单独计算。

由于我们能做的只有同时对所有列组进行循环位移,所以这部分其实就是,所有列组的最小循环节的最小公倍数。

而最小循环节跑一遍 kmp 就能算出来。这里的贡献容易在 复杂度内进行计算。存在某一个列组大小为 的情况类似。

现在我们就只需要考虑所有行和列形成的组大小都 的情况。

称某一个行组与某一个列组对应的所有格子为一个“盘面”,若盘面所有元素都互不相同,则称这个盘面是“互异盘面”,否则为“非互异盘面”。

首先可以发现,一个盘面内的元素永远都不会交换到盘面外头去。

然后可以发现,一个盘面可以在不改变其他盘面的情况下,完成某三个元素的三轮换。

这就说明,对于非互异盘面,可以在不改变其他盘面的情况下,得到所有状态,对于互异盘面,可以在不改变其他盘面的情况下,得到所有奇偶性相同的状态。

那么非互异盘面的问题直接就解决了,只需要考虑互异盘面的情况。

又可以发现,如果有一个盘面,他的长和宽都是奇数,那么无论怎么位移,奇偶性都没有办法改变。

对于某一个大小为奇数的行组,显然对行做操作,只会影响到那些大小为偶数的列组,而影响到的对应盘面,只会被行操作影响,所以这部分贡献,每一个行组是

大小为奇数的列组贡献类似。

那么现在就只需要考虑长和宽均为偶数的互异盘面了。

将行组与列组都看作点,一个互异盘面当作边。

可以发现总方案数其实就是 的生成树边数次方,因为对于每一个环来说,状态的异或和总是

这样就解决了最后一部分的贡献。

时间复杂度可以做到单组

代码

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll lread()
{
ll s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
const int mod=998244353;
bool vis1[1000005];
bool vis2[1000005];
ll jc[3000005];
ll inv[3000005];
int pri[3000005];
int is[3000005];
int wtf[3000005];
int fa[2000005];
vc<int>a[1000005];
vc<int>w1[1000005];
vc<int>w2[1000005];
bool v[1000005];
int p[1000005];
int s[1000005];
int nx[1000005];
int n,m,c1,c2;
vc<int>now;
inline ll qow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inline void init(int L)
{
for(int i=2;i<=L;i++)
{
if(!is[i]) pri[++pri[0]]=i;
for(int j=1;j<=pri[0]&&i*pri[j]<=L;j++)
{
is[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
for(int i=1;i<=pri[0];i++)
{
ll now=pri[i];
while(now<=L) wtf[now]=pri[i],now*=pri[i];
}
memset(is,0,sizeof(is));

jc[0]=1;
for(int i=1;i<=L;i++) jc[i]=jc[i-1]*i%mod;
inv[L]=qow(jc[L],mod-2);
for(int i=L;i;i--) inv[i-1]=inv[i]*i%mod;
for(int i=1;i<=L;i++) inv[i]=jc[i-1]*inv[i]%mod;
}
inline int run(int len)
{
for(int i=2,j=0;i<=len;i++)
{
while(j&&s[j+1]!=s[i]) j=nx[j];
if(s[j+1]==s[i]) j++;
nx[i]=j;
}
int ans=len;
while(nx[len])
{
if(len%(len-nx[len])==0) ans=min(ans,len-nx[len]);
nx[len]=nx[nx[len]];
}
return ans;
}
inline ll push(int v)
{
ll ans=1;
for(int i=1;i<=v;i++) if(v%i==0&&wtf[i]&&!is[i])
{
ans=ans*wtf[i]%mod,is[i]=1;
now.push_back(i);
}
return ans;
}
inline void clear()
{
for(int i:now) is[i]=0;
now.clear();
}
inline int find(int num)
{
if(fa[num]==num) return num;
return fa[num]=find(fa[num]);
}
inline ll get(int x,int y,bool &f)
{
f=0;ll ans=1;int cnt=0;
for(int i:w1[x]) for(int j:w2[y])
{
cnt++,ans=ans*cnt%mod;
if(is[a[i][j]]) f=1;
is[a[i][j]]++,ans=ans*inv[is[a[i][j]]]%mod;
}
for(int i:w1[x]) for(int j:w2[y]) is[a[i][j]]=0;
return ans;
}
int main()
{
int T=read();init(3000000);
while(T--)
{
n=read(),m=read(),c1=c2=0;
for(int i=1;i<=n;i++)
{
a[i].resize(m+1);
for(int j=1;j<=m;j++) a[i][j]=read();
}
for(int i=1;i<=n;i++) p[i]=(i&1)?(i/2+1):((n+i+1)/2),v[i]=0;
for(int i=1;i<=n;i++) if(!v[i])
{
c1++;w1[c1].clear();vis1[c1]=0;
for(int j=i;!v[j];j=p[j]) w1[c1].push_back(j),v[j]=1;
}
for(int i=1;i<=m;i++) p[i]=(i&1)?(i/2+1):((m+i+1)/2),v[i]=0;
for(int i=1;i<=m;i++) if(!v[i])
{
c2++;w2[c2].clear();vis2[c2]=0;
for(int j=i;!v[j];j=p[j]) w2[c2].push_back(j),v[j]=1;
}

for(int i=1;i<=c1+c2;i++) fa[i]=i;

ll ans=1;
for(int i=1;i<=c1;i++) if(w1[i].size()==1)
{
for(int j=1;j<=c2;j++)
{
int len=0;
for(int p:w2[j]) s[++len]=a[w1[i][0]][p];
ans=ans*push(run(len))%mod;
}
clear();
}
for(int i=1;i<=c2;i++) if(w2[i].size()==1)
{
for(int j=1;j<=c1;j++)
{
int len=0;
for(int p:w1[j]) s[++len]=a[p][w2[i][0]];
ans=ans*push(run(len))%mod;
}
clear();
}
for(int i=1;i<=c1;i++) for(int j=1;j<=c2;j++)
{
if(w1[i].size()==1||w2[j].size()==1) continue;
bool f;ll val=get(i,j,f);
if(f) ans=ans*val%mod;
else
{
ans=ans*val%mod*inv[2]%mod;
if(w1[i].size()%2==0&&w2[j].size()%2==0)
{
int u=find(i),v=find(c1+j);
if(u!=v) ans=ans*2%mod,fa[u]=v;
}
else if(w1[i].size()%2==0)
{
if(!vis2[j]) vis2[j]=1,ans=ans*2%mod;
}
else if(w2[j].size()%2==0)
{
if(!vis1[i]) vis1[i]=1,ans=ans*2%mod;
}
}
}
printf("%lld\n",ans);
}
return 0;
}