[ACNOI2022]守序划分问题

题目

题目描述
{ 1 , 2 , 3 , … , n } \{1,2,3,\dots,n\} { 1,2,3,,n} 划分成 m m m 个组,每组中至少有一个数。记为 A 1 , A 2 , … , A m A_1,A_2,\dots,A_m A1,A2,,Am

称一个划分是 “好的”,当且仅当存在排列 p 1 , p 2 , p 3 , … , p m p_1,p_2,p_3,\dots,p_m p1,p2,p3,,pm,令 p 0 = p m p_0=p_m p0=pm 则有 max ⁡ ( A p i ) > min ⁡ ( A p i − 1 )    ( 1 ⩽ i ⩽ m ) \max(A_{p_i})>\min(A_{p_{i-1}})\;(1\leqslant i\leqslant m) max(Api)>min(Api1)(1im)

两个划分本质不同,当且仅当存在两个数字,它们在第一个划分中属于同一集合,而另一个划分中则不属于。

请求出本质不同的划分数量。

数据范围与提示
2 ⩽ m ⩽ n ⩽ 500 2\leqslant m\leqslant n\leqslant 500 2mn500

思路

此题有两种解法。然而就算是有两种,我也没能找到其一;明明是道简单题,我却庸人自扰。就像一条宽阔的柏油路,所有人都觉得十分舒适,偏偏我在马路牙子上骑独轮车 抛彩球,感觉难上加难……

错误伊始

不知道为什么,有一个模糊的念头出现在我的脑海中:肯定是像斯特林数一样,用 f ( i , j ) f(i,j) f(i,j) 表示 i i i 个球 j j j 个盒子的情况,然后直接递推!

一个念头出现了,就根深蒂固。先入为主,十分恐怖。一定要考虑后……

接下来的 1.5 h 1.5h 1.5h,我试过枚举最后一个盒子,然后把它整体删除。失败。试过将它的 min ⁡ \min min 放到前一个盒子中。失败。试过将其整体并入前一个盒子中。失败。

我蚌埠住了。

一误再误

接下来,我有了一个天才的想法,使我在绝望中看到一丝希望:如果 x x x 是该组的 min ⁡ \min min,向下一个集合的 max ⁡ \max max 连边;否则向该组中的前驱连边。这样就一定形成一个环。同时,所有这样连出的环都对应一个划分方案,组数就是 “上升” 的边。

于是我高高兴兴地打了这个做法。虽然对于 O ( n m ) \mathcal O(nm) O(nm) 的复杂度很是怀疑,但也没意识到错误。结果过不了小样例。只好找到所有方案,画出来,找问题……

终于发现:一个合法划分可以对应多个 ⟨ p ⟩ \langle p\rangle p 序列;而我所求的实际上是 ⟨ p ⟩ \langle p\rangle p 的方案。这让我很沮丧——我苦苦探寻,才在黑暗中找到的那点光亮,竟只是枯枝燃烧的一点余烬而已。但它也不能让我气馁:说不定就是要减去重复的情况呢?

现在回看当初,倒不如早些气馁的好。

临终忏悔

终于,我保持着 0 0 0 分的成绩,准备迎接比赛结束了。这时我才想起,解决计数问题,先想判定问题。那么怎样判断一个方案是合法的呢?

我忽然想到一个很简单的方法,按照 max ⁡ \max max 排序,那么就会有 min ⁡ ( A p i ) < max ⁡ ( A p i ) < max ⁡ ( A p i + 1 ) \min(A_{p_i})<\max(A_{p_i})<\max(A_{p_{i+1}}) min(Api)<max(Api)<max(Api+1) 。所以唯一的问题就是 A 1 A_1 A1 A m A_m Am 的关系。

如果 min ⁡ ( A m ) = 1 \min(A_m)=1 min(Am)=1,那就没什么可担心的。所以我们希望前面一段是按照 max ⁡ \max max 升序,走到包含 n n n 的组,然后走到包含 1 1 1 的组。这样就一定可行。而显然这也是必要的。所以,只要包含 1 1 1 的组和包含 n n n 的组可以被 “接” 起来,划分就是合法的。

而我们想象一下这个过程,很容易发现:若对于一个 v ∈ [ 1 , n ) v\in[1,n) v[1,n),使得任意 max ⁡ ( A ) > v \max(A)>v max(A)>v 都有 min ⁡ ( A ) > v \min(A)>v min(A)>v,那就不可行。这相当于上方和下方断裂了。否则,就一定可行,因为总是可以找到一个区间来继续往下走。

把上面的条件再翻译一下,就是 [ min ⁡ , max ⁡ ] [\min,\max] [min,max] 的并集覆盖 [ 0 , n ] [0,n] [0,n] 。很自然地想到容斥了。用 f ( i , j ) f(i,j) f(i,j) 表示将 i i i 个数划分到 j j j 组内的方案数。记 S ( i , j ) S(i,j) S(i,j) 为斯特林数,即将 i i i 个球放到 j j j 个无标号的盒子中,不能空盒的方案数。枚举第一个 “空隙”,那么空隙前和空隙后的数不能在同一个盒子里,否则一定跨过空隙。于是有
f ( n , m ) = S ( n , m ) − ∑ i = 1 n − 1 ∑ j = 1 m − 1 f ( i , j ) S ( n − i , m − j ) f(n,m)=S(n,m)-\sum_{i=1}^{n-1}\sum_{j=1}^{m-1}f(i,j)S(n{\rm-}i,m{\rm-}j) f(n,m)=S(n,m)i=1n1j=1m1f(i,j)S(ni,mj)
其实就是二元生成函数的 F ( x , y ) = S ( x , y ) − F ( x , y ) S ( x , y ) F(x,y)=S(x,y)-F(x,y)S(x,y) F(x,y)=S(x,y)F(x,y)S(x,y) 。不幸的是,二元生成函数无法直接取逆元,因为它是对 x n y m x^ny^m xnym 取模,与一元生成函数很不相同。

所以我们需要每行做 NTT \textit{NTT} NTT,相当于枚举 x x x 的次数,而 y y y 直接进行卷积。令人欣慰的是,每行做 NTT \textit{NTT} NTT 之后不用做逆变换,因为上面的式子是没有取模的;所以总复杂度就是 O ( n m 2 ) \mathcal O(nm^2) O(nm2)可是要写多项式了,坏耶。

别开生面

上面的容斥,确实很自然;但是并不优秀。直接做竟然是最好的选择!

f ( i , j , k ) f(i,j,k) f(i,j,k) 表示,前 i i i 个点已经用了 j j j 条完整的线段,还有 k k k 条线段是有头无尾的。四种情况讨论一下:当前点在线段中间;当前点在线段末尾;当前点是线段开头;当前点自成一格线段。然后就完了。系数很好算;也可参考代码。令 f ( i , j , 0 ) = 0 f(i,j,0)=0 f(i,j,0)=0 即可去除非法状态。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
    
    
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MOD = 998244353;
const int MAXN = 505;
int dp[MAXN][MAXN][MAXN];

int main(){
    
    
	int n = readint(), m = readint();
	dp[0][0][0] = 1;
	rep(i,1,n) rep(j,0,m) rep(k,0,m-j){
    
    
		if(j != m && k == 0){
    
     // gap
			dp[i][j][k] = 0; continue;
		}
		dp[i][j][k] = int((
		// if this one is in the middle, k*dp[j,k]
			llong(k)*dp[i-1][j][k]
		// if this one is the end, (k+1)*dp[j-1,k+1]
			+ (j ? llong(k+1)*dp[i-1][j-1][k+1] : 0)
		// if this one is the head, dp[j,k-1]
			+ (k ? dp[i-1][j][k-1] : 0)
		// if this one is both the head and end, dp[j-1,k]
			+ (j ? dp[i-1][j-1][k] : 0)
		)%MOD);
	}
	printf("%d\n",dp[n][m][0]);
	return 0;
}

后记

比赛结束后一问, O U Y E \sf OUYE OUYE 根本不知道这道题怎么和 “难” 这个字眼有关, D D ( X Y X ) \sf DD(XYX) DD(XYX) 则觉得这种级别的题目不适合拿来做比赛题,干脆打了个 O ( n 2 log ⁡ 2 n ) \mathcal O(n^2\log^2n) O(n2log2n) 的分治 NTT \textit{NTT} NTT 作为娱乐。

为什么我必须被 O U Y E \sf OUYE OUYE 甩开这么远的距离?为什么我总是只能仰望 D D ( X Y X ) \sf DD(XYX) DD(XYX) 的背影?难道我命中注定,必须被 D D ( X Y X ) \sf DD(XYX) DD(XYX) 一次次地击败、打倒、摧毁,被一次次地嘲讽、挖苦、斥责?我看着他们,就像迈特凯看着卡卡西。

D D ( X Y X ) \sf DD(XYX) DD(XYX) 的话又萦绕在耳边:“承认自己强,很难吗?”

猜你喜欢

转载自blog.csdn.net/qq_42101694/article/details/122482797