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;
}

浙公网安备 33010602011771号