AT_arc187_e 做题笔记
zhangshirui
·
2026-11-11 09:03:32
·
题解
显然这序列可以直接看作一个环,然后通过手玩可以注意到一些性质:
只要可以操作,那你可以每次把序列进行旋转 2 ,具体的方式是从一个长至少为 3 的连续段开始,每次把后两个改成下一段的数。发现如果 n 是奇数,这就是任意旋转。并且如果有长为 4 的段,你可以一次转 3 个位置,那也可以凑出来任意旋转。结合下面汇总的操作,可以发现:只要缩完连续段之和段数 k<n-2 那都是可以任意旋转的。
一个同一个数构成的连续段不能够被消除,只能被削减到恰好至少为 1 。
上面一条的衍生结论:只要可以操作,那任意段都可以消为长度恰好 为 1 ,然后使剩下的数集中在一起且相同。具体的操作方法是:从一个长至少为 3 的段开始,只留下一个,多余的全部可以设成他的下一个数。发现这样肯定转一圈回来就把每段多余的删干净了。
然后可以发现,你这样把所有数汇聚起来之后,你可以用同样的操作把数分别插回到每一段之间的空隙中,并设为任意值。操作方法和上面是类似的,只是操作时要保留一些变为插在空隙中的未出现的数。
然后有一些 corner case,就是不能操作就无解,初始排列输出 1 ,全部相同输出 n! ,n=4 的旋转有点特殊也要特殊讨论。以及因为连续段不能消除,所以一个数有两个连续段也无解,下面不讨论这几种情况。
然后就大概可以开始做了。因为你汇聚起来的肯定可以插回去,因此不妨把多的数先全部放在第一段,每次往后面移动插入。这样可以看作这样的一个东西:你初始有一些数,你要在他们之间任意插进去一些数生成出一个排列,并且要满足可以被上面的操作生成,然后这个数列可以任意旋转(不能任意旋转的情况 n 肯定是偶数且旋转是每次挪 2 ,那答案除 2 即可)。
前面是好做的,考虑一个排序什么时候可以被生成。一个充要的条件是存在一对被新插入的数在环上距离不超过 2 。必要性比较显然,通过上面的方式生成,最后一次修改肯定是修改了 3 个连续位置中的两个,那他们肯定距离不超过 2 。充分性呢?考虑你是每次把这些多的数往后挪,顺便把一些数留下来并修改,你填最后的这个位置之前显然可以先转一圈把其他位置填完,然后还剩下至少两个数肯定足够转回来的。因此这个条件是充要的。
问题转化为:你初始有一些数互不相同的数,你要在他们之间任意插进去一些数生成出一个排列,然后插进去的数至少有两个的距离不超过 2 ,数列可以任意旋转。发现这个限制不好做,考虑容斥。
首先是所有情况,首先设原本有 k 连续段,那现在要插入 m=n-k 个数,考虑先把 m 个数确定位置,那剩下的 k 个数就只有旋转的方案了,贡献是 \operatorname{A}_{n}^m\cdot k ,然后拆一下可以得到是 \frac{n!}{(k-1)!} 。
考虑任意两个插入的数之间都需要至少 2 个间隔的方案数。插板法可以得到方案数为 C_{k-m-1}^{m-1} ,然后考虑确定插入数的位置,随便确定一个的位置之后剩下的位置都定下来了,贡献是 n\cdot (m-1)! ,最后原本的数可以旋转,贡献是 k ,全部相乘得 C_{k-m-1}^{m-1}\cdot n\cdot (m-1)!\cdot k 。
最后答案就上面两个相减。
::::success[code]
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
using namespace std;
int n,a[10000005],ans;
ll jc[10000005],inv[10000005];
ll fpow(ll a,int p){return (p?fpow(a*a%mod,p>>1)*(p&1?a:1)%mod:1);}
bool tong[10000005];
ll C(int n,int m){if(n<0||m<0||n<m)return 0;return jc[n]*inv[m]%mod*inv[n-m]%mod;}
int main(){
ios::sync_with_stdio(0);
// freopen("fx.in","r",stdin);
// freopen("fx.out","w",stdout);
cin>>n;
jc[0]=1;
for(int i=1;i<=n;i++)cin>>a[i],jc[i]=jc[i-1]*i%mod;
inv[n]=fpow(jc[n],mod-2);
for(int i=n-1;~i;i--)inv[i]=inv[i+1]*(i+1)%mod;
vector<pair<int,int> > v;
v.reserve(n);
for(int i=1;i<=n;i++){
if(a[i]!=a[i-1])v.push_back({a[i],1});
else v.back().second++;
}
if(v[0].first==v.back().first&&v.size()>1){
v[0].second+=v.back().second;
v.pop_back();
}
for(auto i:v){
if(tong[i.first]){
cout<<0;
return 0;
}
tong[i.first]=1;
}
if(v.size()==1){
ll ans=1;
for(int i=1;i<=n;i++)ans=ans*i%mod;
cout<<ans;
return 0;
}
if(v.size()!=n){
bool bad=1;
for(auto i:v)if(i.second>2)bad=0;
if(bad){
cout<<0;
return 0;
}
}else{
cout<<1;
return 0;
}
if(v.size()==n-2){
if(n!=4)cout<<n*(ll)(n-2)*4/(n&1?1:2)%mod;
else cout<<24<<'\n';
return 0;
}
int k=v.size(),m=n-k;
// ll ans=C(n,m)*jc[m]%mod*k;
// cerr<<jc[n]*inv[k-1]%mod<<'\n';
ll ans=jc[n]*inv[k-1]%mod-n*C(k-m-1,m-1)%mod*jc[m-1]%mod*k%mod+mod;ans%=mod;
cout<<ans*((n&1)||m!=2?1:(mod+1)/2)%mod;
}
::::
附一下我看的鱼教版题解,我觉得讲的还挺细的。看不懂我写的可以看着补充一下,参考的是题解区原本 Acoipp 的题解。
::::info[from deepseek]
下面我按“为什么”的顺序把这份题解和 std 逐段对上讲清楚。先说明:这个环境里没有 g++,所以我把 std 逐句翻译成了 Python,并对工作区里的小样例(fx1–fx6,也就是题面 1/2/3 以及 1 2 3、4 4 4 4、1 2 3 3 3 4)手算了 std 的走向,结果与 .ans 全部一致;下面的推导也都是围绕这个 std 展开的。
0. 先厘清"操作"到底做了什么
一次操作:取三个连续且相等的位置 i,i+1,i+2 ,把其中两个 位置改成 1\sim N 的任意整数。
关键观察(后面所有结论的来源):
没有删除,只有"改值" 。操作只改变某些位置的值,位置本身不消失、不移动。
所以一个值 v 的"副本们"彼此之间的环形相对顺序 永远不会改变;如果你不修改某个副本,它就一直在那里。
一个值 v 的副本,如果中间夹着别的值(即在 A 中 v 的副本不是连续的一段),那么夹在中间的那些值永远不会消失(因为没有"删除"操作,只有把某个位置改成别的值,而那些中间的位置本身也需要三连才能被改)。结论:最终 P 是排列,每个值只出现一次;若 v 在 A 中不连续,P 里只允许一个 v ,但"顺序保留"要求 v 的两个副本分别在夹层两侧,矛盾 ⇒ 答案 0。
这就是 std 里
if(ls[i] && rs[i]-ls[i]+1 != cnt[i]) return cout<<0<<endl,0;
的含义:[\text{ls}_i,\text{rs}_i] 区间长度必须等于 cnt_i ,即该值的所有出现位置连成一段。
配套地,程序先把环转一下(找第一个 a_i\ne a_{i+1} 的位置 i ,整体左移 i ),使得 A_1\ne A_N ,于是"连续段"(块)的定义是唯一、无歧义的。答案与旋转无关,因为操作是在环上定义的。
1. 三种"显然"情况
情况
判据(std 中的写法)
答案
A 本身是排列
所有 cnt_i=1 (alls 变量)
1
所有元素相同
循环找 a_i\ne a_{i+1} ,找不到(i==n+1)
n!
永远无法操作
有任何块不连续
0
没有长度 \ge 3 的块
cnt[i]>=3 从未出现(i>n)
0
最后一条要注意:经过"连续性检查"后还没返回,说明存在某个 cnt\ge 2 ;如果所有 cnt\le 2 ,就永远不会出现三连,于是永远无法操作,答案 0。程序里用 if(cnt[i]>=3) break; 然后 if(i>n) return 0; 实现。
2. 把状态压缩成"块"
经过上面的检查,A 是若干个块拼起来的:每个块内值相同,块与块的值互不相同。设块数 k (cntt),第 i 块长度 s_i ,\sum s_i=n 。
因为最终 P 是排列:
每个旧值最终只剩 1 个副本 (其余都被改掉);
新值(A 中没出现过的数)恰好 m=n-k 个,每个出现一次;
旧值在 P 中仍是"一段"(副本顺序不能乱,也不能被别的旧值插入),且 k 个旧值在 P 中的环形顺序与 A 中相同 。
于是问题变成:在"旧值环序固定"的前提下,把 m 个新数插进去,哪些 P 可达。
3. 分支一:k=n-2 (cntt == n-2),即只有两个新数
此时 \sum s_i = n = k+2 ,多出的 2 个元素只能集中在某一块里:
若分布是 3+1+\cdots+1 :恰有一个长度 3 的块,可以操作;
若是 2+2+1+\cdots :所有块长度 \le 2 ,上一节已判 0。
所以只剩"一个长度 3 的块,其余全为 1"的情形。设这个块是 v,v,v 占位置 p,p+1,p+2 。
只能对它操作:三个位置里留一个 v ,另外两个变成两个新数 x,y (顺序 2 种,留下的是哪一个 3 种)。
所以 x,y 在 P 中的环距离必然 \le 2 (它们来自三个连续位置中的两个)——这是这一步的充要条件。
于是计数只要枚举 x,y 放在哪里:
偶数 n>4 要除以 2 :题解里那段"整体平移 2 位"的意思是,n 为偶数时,每次完整操作都会把图案整体平移 2 位,P 与"P 右移 2 位"是同一个 2 -周期结构被数了两次(n 奇数没有这个对称性);n=4 例外,因为 1,1,1,3\to 3,1,1,1 只平移 1 位,所以 n=4 直接是 4!=24 。
std 里的对应代码:
if(cntt==n-2){
if(n==4){ cout<<24<<endl; return 0; }
ll ans = 4*(n-2)*n%mod;
if(n!=4 && n%2==0) ans = ans*qmi(2,mod-2,mod)%mod;
cout<<ans<<endl; return 0;
}
注意这里的 qmi(2,mod-2,mod) 就是模意义下除以 2。也验证了 fx6(1 2 3 3 3 4,n=6 ):4\cdot4\cdot6/2=48 。
4. 分支二:k\le n-3 (m\ge 3 )—— 本题的核心
4.1 可达性的判据
在"旧值环序固定"的前提下,P 可达 \iff 存在两个新数,它们在 P 中的环距离 \le 2 。
必要性 :看生成 P 的最后一步 。最后一步作用在三个连续相等的位置上,把其中两个改成新数 u,v 。它们位于三个连续位置中的两个,环距离 \le 2 ;之后不再操作,所以 P 中 u,v 的距离仍 \le 2 。
充分性 :若某对位置相距 \le 2 能放新数,其余新数可以按任意顺序"逐步"生成(每次操作生成两个新数时,旧值环序自动保持),最后用那对相距 \le 2 的位置收尾即可。
于是
\text{答案} = \#\{\text{旧值环序保留的 }P\} - \#\{\text{在此基础上所有新数两两环距离}\ge 3\text{ 的 }P\}.
4.2 总数:n!/(k-1)!
- 在环上选 $k$ 个位置放旧值:$\binom{n}{k}$;
- 这 $k$ 个位置的环序固定,但可以任选"起点"($v_1$ 落在哪个位置),共 $k$ 种;
- 剩下的 $m$ 个位置由新值任意填:$m!$ 种。
$$
\binom{n}{k}\cdot k\cdot m! = \frac{n!}{k!\,m!}\cdot k\cdot m! = \frac{n!}{(k-1)!}.
$$
std 里写的是
```cpp
jc[n-1]*inv[cntt-1] // = (n-1)!/(k-1)!
...
cout << ( ... - ans + mod) * n % mod; // 再乘 n,得到 n!/(k-1)!
```
即 `(jc[n-1]*inv[cntt-1])*n = n!/(k-1)!`。
### 4.3 "坏"的个数:容斥 + 隔板法
坏 $P$ = 旧值环序保留,且**任意两个新数环距离 $\ge 3$**。
"环距离 $\ge 3$" 等价于:任意两个相邻新数之间至少隔着 **2 个旧值**(因为它们之间只能放旧值,且旧值不可重复)。
把 $k$ 个旧值按环序分配到 $m$ 个"间隔"里(间隔就是新数之间的空档),每个间隔 $\ge 2$ 个旧值。这就是题解里的
$$
f(n,m)=\binom{n-m-1}{m-1}\quad(\text{把 } n \text{ 个球放入 } m \text{ 个盒子,每个盒子 }\ge 2).
$$
std 把它写成
```cpp
inline ll solve(ll x,ll y){ // 把 x 个球放入 y+1 个盒子,每盒 >=2
x -= (y+1);
return C(x-1,y); // = C(x-y-2, y) = f(x, y+1)
}
```
调用时 `y = n-cntt-1 = m-1`,也就是"$m$ 个盒子"。
为了避免"环上整体旋转"造成重复计数,枚举**第一个新数**的落点:它落在第 $i$ 个旧块之后($i=1..k$)。此时能参与分配的旧值个数为 `ball`:
$$
\text{ball} = \begin{cases} k, & i=1,\\ k-i+2, & i\ge 2.\end{cases}
$$
(std 里 `ball=cntt-i+1; if(i>1) ball++;` 正是这个。)对应的坏方案数贡献为
$$
m!\cdot f(\text{ball},\,m) = m!\cdot\binom{\text{ball}-m-1}{m-1},
$$
累加得到 $B$。`ball` 太小时组合数为 0($C$ 函数对非法下标返回 0),这也解释了样例 1、样例 3 里 $B=0$。
std 里:
```cpp
ll ans = 0;
for(i=1;i<=cntt;i++){
ll ball = cntt-i+1;
if(i==1) ball+=0; else ball+=1;
ans = (ans + jc[n-cntt]*solve(ball, n-cntt-1)) % mod;
}
```
`jc[n-cntt] = m!`,`solve(ball, m-1)` 就是上面的 $f(\text{ball},m)$。
### 4.4 合并
$$
\text{answer}=\left(\frac{n!}{(k-1)!}-B\right)\bmod 998244353
=\big(\texttt{jc[n-1]*inv[cntt-1]}-B\big)\cdot n \bmod p .
$$
---
## 5. 用 std 走一遍样例,确认理解
**样例 1**:`1 2 3 3 1 1`,$n=6$。
- $a_1\ne a_2$,旋转后为 `2 3 3 1 1 1`;块:`[2] [3,3] [1,1,1]`,连续性检查通过,$k=3$。
- $k=3\ne n-2=4$,进入核心分支,$m=3$。
- $\text{ball}$:$i=1\to3$,$i=2\to3$,$i=3\to2$;`solve(3,2)=C(-1,2)=0`,`solve(2,2)=C(-2,2)=0`,故 $B=0$。
- 答案 $=\frac{6!}{2!}=360$。✓
**样例 2**:`3 1 3 4 1`,值 1 出现在位置 2、5,中间夹着 3、4 ⇒ 不连续 ⇒ 0。✓
**样例 3**:`1 1 1 8 8 8 7 7 7 10`,旋转后块长 $3,3,3,1$,$k=4$,$m=6$,$\text{ball}\le 4<2m$ ⇒ $B=0$,答案 $\frac{10!}{3!}=604800$。✓
**fx6**:`1 2 3 3 3 4`,块长 $1,1,3,1$,$k=4=n-2$,$n=6$ 偶数 ⇒ $4\cdot4\cdot6/2=48$。✓
**fx4**:`1 2 3` 是排列 ⇒ 1;**fx5**:全相等 ⇒ $4!=24$。✓
---
## 6. 容易踩的坑(也是题解里表述含糊的地方)
1. 题解说"没有在 $A$ 中间的两个数字",其实是"**没有在 $A$ 中出现的**两个数字"(即两个新数)——笔误。
2. "相对位置保留"要理解成:**旧值的副本按 $A$ 中的环形顺序排列,且同一个旧值的副本仍连成一段**。因为最终是排列,每个旧值只剩一个副本,所以只需保证 $k$ 个旧值的环序。
3. 被替换的两个数**可以相等,也可以是已经出现过的数**(题目只说"替换成 $1\sim N$ 的整数")。这正是 std 里 $C$ 的下标可能为负、返回 0 的原因:把"球数不够"(即间隔无法都塞满 2 个旧值)的情形自然排除。
4. $n=4$ 必须特判:平移 1 位可达,导致"除以 2"不成立,直接 $24$。
5. 复杂度:预处理阶乘/逆元、一次线性扫描,$O(n)$ 时间、$O(n)$ 空间。
---
## 7. 一句话总结各分支
设 $k$ 为块数(不同值的个数):
- 有值不连续 → $0$;全是 1 次 → $1$;全相同 → $n!$;最大块长 $\le 2$ → $0$;
- $k=n-2$:$n=4$ 时为 $24$,否则 $4(n-2)n$($n>4$ 偶数再除 2);
- $k\le n-3$:$\dfrac{n!}{(k-1)!}-\sum_{i=1}^{k} m!\,f(\text{ball}_i,m)$,其中 $m=n-k$,$\text{ball}_i=k-i+2\ (i\ge2)$、$\text{ball}_1=k$,$f(x,m)=\binom{x-m-1}{m-1}$。
这就是 std 的完整逻辑;三组样例和 fx4–fx6 都对得上。下面我按“为什么”的顺序把这份题解和 std 逐段对上讲清楚。先说明:这个环境里没有 `g++`,所以我把 std 逐句翻译成了 Python,并对工作区里的小样例(fx1–fx6,也就是题面 1/2/3 以及 `1 2 3`、`4 4 4 4`、`1 2 3 3 3 4`)手算了 std 的走向,结果与 `.ans` 全部一致;下面的推导也都是围绕这个 std 展开的。
---
## 0. 先厘清"操作"到底做了什么
一次操作:取三个连续且相等的位置 $i,i+1,i+2$,把其中**两个**位置改成 $1\sim N$ 的任意整数。
关键观察(后面所有结论的来源):
1. **没有删除,只有"改值"**。操作只改变某些位置的值,位置本身不消失、不移动。
2. 所以一个值 $v$ 的"副本们"彼此之间的**环形相对顺序**永远不会改变;如果你不修改某个副本,它就一直在那里。
3. 一个值 $v$ 的副本,如果中间夹着别的值(即在 $A$ 中 $v$ 的副本不是连续的一段),那么夹在中间的那些值永远不会消失(因为没有"删除"操作,只有把某个位置改成别的值,而那些中间的位置本身也需要三连才能被改)。**结论:最终 $P$ 是排列,每个值只出现一次;若 $v$ 在 $A$ 中不连续,$P$ 里只允许一个 $v$,但"顺序保留"要求 $v$ 的两个副本分别在夹层两侧,矛盾 ⇒ 答案 0。**
这就是 std 里
```cpp
if(ls[i] && rs[i]-ls[i]+1 != cnt[i]) return cout<<0<<endl,0;
```
的含义:$[\text{ls}_i,\text{rs}_i]$ 区间长度必须等于 $cnt_i$,即该值的所有出现位置连成一段。
配套地,程序先把环转一下(找第一个 $a_i\ne a_{i+1}$ 的位置 $i$,整体左移 $i$),使得 $A_1\ne A_N$,于是"连续段"(块)的定义是唯一、无歧义的。答案与旋转无关,因为操作是在环上定义的。
---
## 1. 三种"显然"情况
| 情况 | 判据(std 中的写法) | 答案 |
|---|---|---|
| $A$ 本身是排列 | 所有 $cnt_i=1$(`alls` 变量) | $1$ |
| 所有元素相同 | 循环找 $a_i\ne a_{i+1}$,找不到(`i==n+1`) | $n!$ |
| 永远无法操作 | 有任何块不连续 | $0$ |
| 没有长度 $\ge 3$ 的块 | `cnt[i]>=3` 从未出现(`i>n`) | $0$ |
最后一条要注意:经过"连续性检查"后还没返回,说明存在某个 $cnt\ge 2$;如果所有 $cnt\le 2$,就永远不会出现三连,于是永远无法操作,答案 0。程序里用 `if(cnt[i]>=3) break;` 然后 `if(i>n) return 0;` 实现。
---
## 2. 把状态压缩成"块"
经过上面的检查,$A$ 是若干个块拼起来的:每个块内值相同,块与块的值互不相同。设块数 $k$(`cntt`),第 $i$ 块长度 $s_i$,$\sum s_i=n$。
因为最终 $P$ 是排列:
- 每个旧值最终**只剩 1 个副本**(其余都被改掉);
- 新值($A$ 中没出现过的数)恰好 $m=n-k$ 个,每个出现一次;
- 旧值在 $P$ 中仍是"一段"(副本顺序不能乱,也不能被别的旧值插入),且 $k$ 个旧值在 $P$ 中的**环形顺序与 $A$ 中相同**。
于是问题变成:在"旧值环序固定"的前提下,把 $m$ 个新数插进去,哪些 $P$ 可达。
---
## 3. 分支一:$k=n-2$(`cntt == n-2`),即只有两个新数
此时 $\sum s_i = n = k+2$,多出的 2 个元素只能集中在某一块里:
- 若分布是 $3+1+\cdots+1$:恰有一个长度 3 的块,可以操作;
- 若是 $2+2+1+\cdots$:所有块长度 $\le 2$,上一节已判 0。
所以只剩"一个长度 3 的块,其余全为 1"的情形。设这个块是 $v,v,v$ 占位置 $p,p+1,p+2$。
- 只能对它操作:三个位置里留一个 $v$,另外两个变成两个新数 $x,y$(顺序 2 种,留下的是哪一个 3 种)。
- 所以 $x,y$ 在 $P$ 中的**环距离必然 $\le 2$**(它们来自三个连续位置中的两个)——这是这一步的充要条件。
- 于是计数只要枚举 $x,y$ 放在哪里:
- $x$ 有 $n$ 个位置;$y$ 要在它左右各 2 个位置里选(距离 $\le 2$),共 4 种;再乘"块的位置"带来的 $n-2$ 种,得到 $4(n-2)n$。
- **偶数 $n>4$ 要除以 2**:题解里那段"整体平移 2 位"的意思是,$n$ 为偶数时,每次完整操作都会把图案整体平移 2 位,$P$ 与"$P$ 右移 2 位"是同一个 $2$-周期结构被数了两次($n$ 奇数没有这个对称性);$n=4$ 例外,因为 $1,1,1,3\to 3,1,1,1$ 只平移 1 位,所以 $n=4$ 直接是 $4!=24$。
std 里的对应代码:
```cpp
if(cntt==n-2){
if(n==4){ cout<<24<<endl; return 0; }
ll ans = 4*(n-2)*n%mod;
if(n!=4 && n%2==0) ans = ans*qmi(2,mod-2,mod)%mod;
cout<<ans<<endl; return 0;
}
```
注意这里的 `qmi(2,mod-2,mod)` 就是模意义下除以 2。也验证了 fx6(`1 2 3 3 3 4`,$n=6$):$4\cdot4\cdot6/2=48$。
---
## 4. 分支二:$k\le n-3$($m\ge 3$)—— 本题的核心
### 4.1 可达性的判据
> 在"旧值环序固定"的前提下,$P$ 可达 $\iff$ 存在两个新数,它们在 $P$ 中的环距离 $\le 2$。
- **必要性**:看生成 $P$ 的**最后一步**。最后一步作用在三个连续相等的位置上,把其中两个改成新数 $u,v$。它们位于三个连续位置中的两个,环距离 $\le 2$;之后不再操作,所以 $P$ 中 $u,v$ 的距离仍 $\le 2$。
- **充分性**:若某对位置相距 $\le 2$ 能放新数,其余新数可以按任意顺序"逐步"生成(每次操作生成两个新数时,旧值环序自动保持),最后用那对相距 $\le 2$ 的位置收尾即可。
于是
$$
\text{答案} = \#\{\text{旧值环序保留的 }P\} - \#\{\text{在此基础上所有新数两两环距离}\ge 3\text{ 的 }P\}.
$$
### 4.2 总数:$n!/(k-1)!
- 在环上选 $k$ 个位置放旧值:$\binom{n}{k}$;
- 这 $k$ 个位置的环序固定,但可以任选"起点"($v_1$ 落在哪个位置),共 $k$ 种;
- 剩下的 $m$ 个位置由新值任意填:$m!$ 种。
$$
\binom{n}{k}\cdot k\cdot m! = \frac{n!}{k!\,m!}\cdot k\cdot m! = \frac{n!}{(k-1)!}.
$$
std 里写的是
```cpp
jc[n-1]*inv[cntt-1] // = (n-1)!/(k-1)!
...
cout << ( ... - ans + mod) * n % mod; // 再乘 n,得到 n!/(k-1)!
```
即 `(jc[n-1]*inv[cntt-1])*n = n!/(k-1)!`。
### 4.3 "坏"的个数:容斥 + 隔板法
坏 $P$ = 旧值环序保留,且**任意两个新数环距离 $\ge 3$**。
"环距离 $\ge 3$" 等价于:任意两个相邻新数之间至少隔着 **2 个旧值**(因为它们之间只能放旧值,且旧值不可重复)。
把 $k$ 个旧值按环序分配到 $m$ 个"间隔"里(间隔就是新数之间的空档),每个间隔 $\ge 2$ 个旧值。这就是题解里的
$$
f(n,m)=\binom{n-m-1}{m-1}\quad(\text{把 } n \text{ 个球放入 } m \text{ 个盒子,每个盒子 }\ge 2).
$$
std 把它写成
```cpp
inline ll solve(ll x,ll y){ // 把 x 个球放入 y+1 个盒子,每盒 >=2
x -= (y+1);
return C(x-1,y); // = C(x-y-2, y) = f(x, y+1)
}
```
调用时 `y = n-cntt-1 = m-1`,也就是"$m$ 个盒子"。
为了避免"环上整体旋转"造成重复计数,枚举**第一个新数**的落点:它落在第 $i$ 个旧块之后($i=1..k$)。此时能参与分配的旧值个数为 `ball`:
$$
\text{ball} = \begin{cases} k, & i=1,\\ k-i+2, & i\ge 2.\end{cases}
$$
(std 里 `ball=cntt-i+1; if(i>1) ball++;` 正是这个。)对应的坏方案数贡献为
$$
m!\cdot f(\text{ball},\,m) = m!\cdot\binom{\text{ball}-m-1}{m-1},
$$
累加得到 $B$。`ball` 太小时组合数为 0($C$ 函数对非法下标返回 0),这也解释了样例 1、样例 3 里 $B=0$。
std 里:
```cpp
ll ans = 0;
for(i=1;i<=cntt;i++){
ll ball = cntt-i+1;
if(i==1) ball+=0; else ball+=1;
ans = (ans + jc[n-cntt]*solve(ball, n-cntt-1)) % mod;
}
```
`jc[n-cntt] = m!`,`solve(ball, m-1)` 就是上面的 $f(\text{ball},m)$。
### 4.4 合并
$$
\text{answer}=\left(\frac{n!}{(k-1)!}-B\right)\bmod 998244353
=\big(\texttt{jc[n-1]*inv[cntt-1]}-B\big)\cdot n \bmod p .
$$
---
## 5. 用 std 走一遍样例,确认理解
**样例 1**:`1 2 3 3 1 1`,$n=6$。
- $a_1\ne a_2$,旋转后为 `2 3 3 1 1 1`;块:`[2] [3,3] [1,1,1]`,连续性检查通过,$k=3$。
- $k=3\ne n-2=4$,进入核心分支,$m=3$。
- $\text{ball}$:$i=1\to3$,$i=2\to3$,$i=3\to2$;`solve(3,2)=C(-1,2)=0`,`solve(2,2)=C(-2,2)=0`,故 $B=0$。
- 答案 $=\frac{6!}{2!}=360$。✓
**样例 2**:`3 1 3 4 1`,值 1 出现在位置 2、5,中间夹着 3、4 ⇒ 不连续 ⇒ 0。✓
**样例 3**:`1 1 1 8 8 8 7 7 7 10`,旋转后块长 $3,3,3,1$,$k=4$,$m=6$,$\text{ball}\le 4<2m$ ⇒ $B=0$,答案 $\frac{10!}{3!}=604800$。✓
**fx6**:`1 2 3 3 3 4`,块长 $1,1,3,1$,$k=4=n-2$,$n=6$ 偶数 ⇒ $4\cdot4\cdot6/2=48$。✓
**fx4**:`1 2 3` 是排列 ⇒ 1;**fx5**:全相等 ⇒ $4!=24$。✓
---
## 6. 容易踩的坑(也是题解里表述含糊的地方)
1. 题解说"没有在 $A$ 中间的两个数字",其实是"**没有在 $A$ 中出现的**两个数字"(即两个新数)——笔误。
2. "相对位置保留"要理解成:**旧值的副本按 $A$ 中的环形顺序排列,且同一个旧值的副本仍连成一段**。因为最终是排列,每个旧值只剩一个副本,所以只需保证 $k$ 个旧值的环序。
3. 被替换的两个数**可以相等,也可以是已经出现过的数**(题目只说"替换成 $1\sim N$ 的整数")。这正是 std 里 $C$ 的下标可能为负、返回 0 的原因:把"球数不够"(即间隔无法都塞满 2 个旧值)的情形自然排除。
4. $n=4$ 必须特判:平移 1 位可达,导致"除以 2"不成立,直接 $24$。
5. 复杂度:预处理阶乘/逆元、一次线性扫描,$O(n)$ 时间、$O(n)$ 空间。
---
## 7. 一句话总结各分支
设 $k$ 为块数(不同值的个数):
- 有值不连续 → $0$;全是 1 次 → $1$;全相同 → $n!$;最大块长 $\le 2$ → $0$;
- $k=n-2$:$n=4$ 时为 $24$,否则 $4(n-2)n$($n>4$ 偶数再除 2);
- $k\le n-3$:$\dfrac{n!}{(k-1)!}-\sum_{i=1}^{k} m!\,f(\text{ball}_i,m)$,其中 $m=n-k$,$\text{ball}_i=k-i+2\ (i\ge2)$、$\text{ball}_1=k$,$f(x,m)=\binom{x-m-1}{m-1}$。
这就是 std 的完整逻辑;三组样例和 fx4–fx6 都对得上。
::::