[题解]CF1811G2 Vlad and the Nice Paths (hard version)

因为我是彩笔,所以我不会组合数,只会暴力。

思路

由 Easy Version 得到的状态转移方程:

\[ dp_{i,j} = \left\{\begin{matrix} \sum_{p = 1}^{p < i}{dp_{p,j - 1}} & (j \bmod k = 1)\\ \sum_{p = 1}^{p < i \wedge c_p = c_i}{dp_{p,j - 1}} & (\operatorname{otherwise}) \end{matrix}\right. \]

然后你发现对于上面的式子你可以用一个前缀和优化成 \(\Theta(n^2)\) 的,对于第二个式子你考虑也运用类似与前缀和的方式解决。

定义 \(f_{i,j}\) 表示,当前取出 \(j\) 个颜色为 \(i\) 的瓷砖的方案数,于是第二个式子就可以转化成 \(f_{c_i,j - 1}\)。然后对于每一个 \(i\),都可以将 \(f_{c_i,j}\) 更新为 \(f_{c_i,j} + dp_{i,j}\)

毒瘤警告:有毒瘤 Hack 使得答案的总和模 \(10^9 + 7\) 等于 \(0\),所以你还需要额外的标记数组判断这个位置是为是真的有值。

Code

#include <bits/stdc++.h>  
#define re register  
#define Add(a,b) (((a) + (b)) % mod)  
  
using namespace std;  
  
const int N = 5010,mod = 1e9 + 7;  
int n,k;  
int arr[N],dp[N][N],s[N][N],f[N][N];  
bool sdp[N][N],ss[N][N],sf[N][N];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 3) + (r << 1) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline void solve(){  
    dp[0][0] = s[0][0] = 1;  
    sdp[0][0] = ss[0][0] = true;  
    n = read();  
    k = read();  
    for (re int i = 1;i <= n;i++) arr[i] = read();  
    for (re int i = 1;i <= n;i++){  
        for (re int j = 0;j <= n;j++){  
            dp[i][j] = s[i][j] = f[i][j] = 0;  
            sdp[i][j] = ss[i][j] = sf[i][j] = false;  
        }  
    }  
    for (re int i = 1;i <= n;i++){  
        for (re int j = i;~j;j--){  
            if (j % k == 1){  
                dp[i][j] = Add(dp[i][j],s[i - 1][j - 1]);  
                sdp[i][j] |= ss[i - 1][j - 1];  
            }  
            else if (j){  
                dp[i][j] = Add(dp[i][j],f[arr[i]][j - 1]);  
                sdp[i][j] |= sf[arr[i]][j - 1];  
            }  
            s[i][j] = Add(s[i - 1][j],dp[i][j]);  
            ss[i][j] |= ss[i - 1][j] | sdp[i][j];  
            f[arr[i]][j] = Add(f[arr[i]][j],dp[i][j]);  
            sf[arr[i]][j] |= sdp[i][j];  
        }  
    }  
    for (re int j = n;~j;j--){  
        if (j % k == 0){  
            int ans = 0;  
            bool falg = false;  
            for (re int i = 1;i <= n;i++){  
                ans = Add(ans,dp[i][j]);  
                falg |= sdp[i][j];  
            }  
            if (falg){  
                printf("%d\n",ans);  
                return;  
            }  
        }  
    }  
    puts("1");  
}  
  
int main(){  
    int T;  
    T = read();  
    while (T--) solve();  
    return 0;  
}  
posted @ 2024-06-25 12:27  WBIKPS  阅读(22)  评论(0)    收藏  举报