UTS Open '21 P7 - April Fools 题解

UTS Open '21 P7 - April Fools 题解


知识点

插入计数 DP。

分析

首先可以看出,这道题目是一道计数 DP,且在贪心地升序排序之后,我们的问题会变得更好求解。

其次,一个数据范围在 \(O(n^3)\) 的排列计数题,很容易想到插入 DP,那么我们就有了基本的思路:

  • 维护连续段。
  • 维护连续段的末尾,因为这个不一定可以像开头一样直接加。
  • 维护最后一段的末尾。

状态定义

\(f_{i,j,k,0/1/2}\) 表示前 \(i\) 个元素,非最后一段有 \(j\) 段末尾为 \(\mathrm{MSB}(A_i)\),非最后一段有 \(k\) 段末尾为 \(\mathrm{MSB}(A_i)-1\),最后一段末尾为 \(\mathrm{MSB}(A_i)/\mathrm{MSB}(A_i)-1/ < \mathrm{MSB}(A_i)-1\) 的方案数。

初态

初始状态为 \(f_{1,0,0,0}=1\)

转移

注:以下状态省去第一维,类似滚动数组写法,用 \(f,f'\) 表示更新前后状态;\(s\in[0,2]\) 用以表示最后一维。

假设现在从 \(A_i\) 转移到 \(A_{i+1}\),考虑一个个分析过来。

  • \(\mathrm{MSB}(A_i) = \mathrm{MSB}(A_{i+1})\)

    • 插入(insert):

      在前 \(j+k\) 个段前后:

      \[(j+k+1)f_{j,k,s} \to f'_{j+1,k,s} \\ \]

      在最后一段之后:

      \[f_{j,k,0} \to f'_{j+1,k,0} \\ f_{j,k,1} \to f'_{j,k+1,0} \\ \]

    • 拓展(extend):

      在某个段之前:

      \[(j+k+1)f_{j,k,s} \to f'_{j,k,s} \\ \]

      在前 \(k\) 段之后:

      \[k\times f_{j,k,s} \to f'_{j+1,k-1,s} \\ \]

      在最后一段之后:

      \[f_{j,k,1} \to f'_{j,k,0} \\ \]

    • 合并(merge):

      \[k\times f_{j,k,s} \to f'_{j,k-1,s} \\ \]

  • \(\mathrm{MSB}(A_i) = \mathrm{MSB}(A_{i+1}) - 1\)

    此时 \(k\) 必为 \(0\)\(s’=\min(2,s+1)\)

    • 插入(insert):

      在前 \(j\) 段前后:

      \[(j+1)f_{j,0,s} \to f'_{1,j,s'} \\ \]

      在最后一段之后:

      \[f_{j,0,0} \to f'_{0,j+1,0} \\ \]

    • 拓展(extend):

      在某段之前:

      \[(j+1)f_{j,0,s} \to f'_{0,j,s'} \\ \]

      在前 \(j\) 段之后:

      \[j\times f_{j,0,s} \to f'_{1,j-1,s'} \\ \]

      在最后一段之后:

      \[f_{j,0,0} \to f'_{0,j,0} \\ \]

    • 合并(merge):

      \[j\times f_{j,0,s} \to f'_{0,j-1,s'} \\ \]

  • \(\mathrm{MSB}(A_i) < \mathrm{MSB}(A_{i+1}) - 1\)

    此时 \(j,k\) 都必为 \(0\)

    • 插入(insert):一定在前面:

      \[f_{0,0,s} \to f'_{1,0,2} \\ \]

    • 拓展(extend):一定在左边:

      \[f_{0,0,s} \to f'_{0,0,2} \\ \]

统计

最终答案为 \(\sum_{s=0}^2 f_{n,0,0,s}\)

代码

编码时需要循环展开以减小常数。

constexpr int N(5e2+10);

int n;

int f[N][N][3],_f[N][N][3];

void trs(int _j,int _k,int _s,int j,int k,int s,const int d=1) {
	toadd(_f[_j][_k][_s],mul(d,f[j][k][s]));
}

#define Trs_1(_j,_k,j,k,d) \
	trs(_j,_k,0,j,k,0,d),trs(_j,_k,1,j,k,1,d),trs(_j,_k,2,j,k,2,d)

void Trans_1(const int lim) { // A_{i} = A_{i+1}
	FOR(j,0,lim-1)FOR(k,0,lim-1-j) {
		// insert
		// - front j + k
		Trs_1(j+1,k,j,k,j+k+1);
		// - last
		trs(j+1,k,0,j,k,0);
		trs(j,k+1,0,j,k,1);
		// extend
		// - before
		Trs_1(j,k,j,k,j+k+1);
		// - after front k
		if(k)Trs_1(j+1,k-1,j,k,k);
		// - after last
		trs(j,k,0,j,k,1);
		// merge
		if(k)Trs_1(j,k-1,j,k,k);
	}
}

#define Trs_2(_j,_k,j,k,d) \
	trs(_j,_k,1,j,k,0,d),trs(_j,_k,2,j,k,1,d),trs(_j,_k,2,j,k,2,d)

void Trans_2(const int lim) { // A_{i} = A_{i+1} - 1
	FOR(j,0,lim-1) {
		// insert
		// - front j
		Trs_2(1,j,j,0,j+1);
		// - last
		trs(0,j+1,0,j,0,0);
		// extend
		// - before
		Trs_2(0,j,j,0,j+1);
		// - after front j
		if(j)Trs_2(1,j-1,j,0,j);
		// - after last
		trs(0,j,0,j,0,0);
		// merge
		if(j)Trs_2(0,j-1,j,0,j);
	}
}

void Trans_3(const int lim) { // A_{i} < A_{i+1} - 1
	// insert
	FOR(s,0,2)trs(1,0,2,0,0,s);
	// extend
	FOR(s,0,2)trs(0,0,2,0,0,s);
}

int ans;

signed main() {
	/*DE("Input");*/
	cin>>n;
	FOR(i,1,n)cin>>a[i];
	/*DE("Init");*/
	sort(a+1,a+n+1);
	FOR(i,1,n)a[i]=__builtin_clz(a[i]);
	/*DE("DP");*/
	f[0][0][0]=1;
	FOR(i,1,n-1) {
		// clear
		FOR(j,0,i)FOR(k,0,i-j)FOR(s,0,2)_f[j][k][s]=0;
		// dp
		if(a[i]==a[i+1])Trans_1(i);
		else if(a[i]==a[i+1]-1)Trans_2(i);
		else Trans_3(i);
		// swap
		swap(f,_f);
	}
	/*DE("Count");*/
	FOR(s,0,2)toadd(ans,f[0][0][s]);
	/*DE("Output");*/
	cout<<ans<<endl;
	return 0;
}
posted @ 2025-11-25 22:17  Add_Catalyst  阅读(1)  评论(0)    收藏  举报