题解:P17284 「IXOI R2」想不出来
思路
先观察合法的 \(p\) 有什么性质:
-
\(\sum_{i=1}^{n}p_i=n\)。这是显然的。
-
\(\forall p_i\geq 2,p_{i-1}=p_{i+1}=0\)。因为要让 \(p_i\geq 2\) 必须要对 \(i\) 位置做一次操作,这会使 \(p_{i-1}=p_{i+1}=0\),而此后也不能再对 \(i-1\) 或 \(i+1\) 位置操作。
-
\(\forall p_i=p_{i+1}=1,\sum_{j=1}^{i}p_j=i\)。换句话说,就是连续的 \(1\) 序列不能平移(否则必然会导致合并),且其他数不能越过连续的 \(1\) 序列。
事实上,满足这三条性质的所有 \(p\) 都是可以取到的,操作方法大致为这样:去掉不动的连续 \(1\) 序列,只需要对被分开的每一段分别操作;每一段中将连续的 \(1\) 合并成块,然后把块移动到对应的位置即可。
然后就容易想到 dp:设 \(f_{i,j,0/1/2}\) 分别表示填了前 \(i\) 个位置,已经用了 \(j\) 个 \(1\),且 \(i\) 位置填的是 \(0\),\(1\) 或大于等于 \(2\) 的数的权值和。初状态为 \(f_{0,0,0}=1\),最终答案为 \(f_{n,n,0}+f_{n,n,1}+f_{n,n,2}\),转移如下:
直接做的时间复杂度是 \(O(n^3)\)。注意到 \(\sum_{k=0}^{j-2}f_{i-1,k,0}\times a_i^{j-k}\) 这个值在 \(j\) 每次加 \(1\) 时只会乘 \(a_i\) 再加上 \(f_{i-1,j-2,0}\times a_i^2\),就不用每次都求和了。
最终的时间复杂度为 \(O(n^2)\)。第一维可以滚动数组优化,所以空间复杂度可做到 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=8005,P=1e9+7;
int n,f[2][N][3];
int main()
{
scanf("%d",&n);
f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
int v;
scanf("%d",&v);
int id=i&1,sum=0;
memset(f[id],0,sizeof(f[id]));
for(int j=0;j<=n;j++)
{
f[id][j][0]=((f[!id][j][0]+f[!id][j][1])%P+f[!id][j][2])%P;
if(j)
{
f[id][j][1]=1ll*f[!id][j-1][0]*v%P;
if(i==j)
(f[id][j][1]+=1ll*f[!id][j-1][1]*v%P)%=P;
}
if(j>1)
{
sum=(1ll*sum*v%P+f[!id][j-2][0])%P;
f[id][j][2]=1ll*sum*v%P*v%P;
}
}
}
printf("%d",((f[n&1][n][0]+f[n&1][n][1])%P+f[n&1][n][2])%P);
return 0;
}

浙公网安备 33010602011771号