题解:P17284 「IXOI R2」想不出来

题目链接

思路

先观察合法的 \(p\) 有什么性质:

  1. \(\sum_{i=1}^{n}p_i=n\)。这是显然的。

  2. \(\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\) 位置操作。

  3. \(\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}\),转移如下:

\[f_{i,j,0}=f_{i-1,j,0}+f_{i-1,j,1}+f_{i-1,j,2} \]

\[f_{i,j,1}=(f_{i-1,j-1,0}+[i=j]f_{i-1,j-1,1})\times a_i \]

\[f_{i,j,2}=\sum_{k=0}^{j-2}f_{i-1,k,0}\times a_i^{j-k} \]

直接做的时间复杂度是 \(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;
}
posted @ 2026-08-24 18:55  ShanLing3  阅读(7)  评论(0)    收藏  举报
//雪花飘落效果