CF1093F Vasya and Array DP

題面

題面
\(\Delta\)題面有點問題,應該是數列中沒有長度大於等於\(len\)的連續數字纔是合法的.

題解

\(f[i][j]\)表示DP到\(i\)位,以\(j\)爲結尾的方案數, \(sum[i]\)表示\(\sum_{j = 1}^{k}f[i][j]\), \(g[i][j]\)表示第\(i\)位爲結尾,當前段全都是數字\(j\)的最長長度(不考慮\(len\)的限制,能延長就儘量延長,你可以理解爲把\(1\)\(i\)\(-1\)全都改成\(j\),然後再看第\(i\)位以\(j\)爲結尾的連續數字有多長)。
那麼有:
\[f[i][j] = \begin{cases} 0 \quad s_i \ne -1 \ and \ s_i \ne j \\ sum[i - 1] \quad g[i][j] < len \\ sum[i - 1] - (sum[i - len] - f[i - len][j]) \quad others \end{cases}\]
第一種轉移比較簡單,解釋下最後兩種。
第二種:
因爲\(g[i][j] < len\),所以在第\(i\)位,以\(j\)結尾時,不管前面是什麼情況,肯定都合法,所以直接加上上一位總的方案就可以了

第三種:(以下所說的連續數字含義均爲連續相同數字)
先明確一點:對於一段長度大於等於\(len\)的連續數字而言,我們只會在它的第一個不合法位置減去它的方案數,例如一個長爲\(len + k\)的連續數字,我們只會在它的第\(len\)位減去它的方案數。
顯然這樣可以保證不重不漏,也就相當於其實我們每次減去的都是以某個固定位置開頭的不合法連續數字的方案數,所以肯定不會有重複和遺漏的。
那爲什麼不每次減去以某個固定位置結尾的不合法連續數字的方案數呢?
因爲我們是從前向後DP的,所以對於前面一個固定位置的一些信息,我們已經處理出來了,對於以某個固定位置結尾的不合法連續數字而言,當我們DP到這個結尾位置的時候,這個地方的值正我們需要計算的,總不能自己調用自己吧。

再考慮計算:
首先\(sum[i - 1]\)是總的方案數,但是其中有一部分方案不合法,因爲當前段長度大於等於\(len\).
因此我們再考慮如何計算不合法的方案。
根據我們的策略,對於一段長度大於等於\(len\)的連續數字,我們只會在它的第\(len\)位減去它的方案數。
也就是我們只能減去長度爲\(len\)的連續數字
因此我們假定從第\(i\)位開始,向前\(len\)個都是\(j\).(注意此時\(g[i][j] >= len\),所以一定有方案可以使得從\(i\)向前\(len\)個都是\(j\))
那麼因爲已經不合法了,所以從第\(i - len\)位開始,往前走的就都可以任取,所以總方案數爲\(sum[i - len]\).
但是我們只能統計長度爲\(len\)的連續數字,所以第\(i - len\)位不能是\(j\),否則就會接在以前變成一段長度大於\(len\)的連續數字了。
因此我們還要減去以\(i - len\)爲結尾的,結尾數字爲\(j\)的方案數。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define p 998244353
#define AC 101000
#define ac 110

int n, k, len, ans;
int s[AC], f[AC][ac], g[AC][ac], sum[AC];

inline int read()
{
    int x = 0;char c = getchar();bool zz = false;
    while(c > '9' || c < '0') {if(c == '-') zz = true; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return zz ? -x : x;
} 

inline void up(int &a, int b) {a += b; if(a < 0) a += p; if(a >= p) a-= p;}
inline int ad(int a, int b) {a += b; if(a < 0) a += p; if(a >= p) a -= p; return a;}
inline int mul(int a, int b) {return 1LL * a * b % p;}

void pre()
{
    n = read(), k = read(), len = read();
    for(R i = 1; i <= n; i ++) s[i] = read();
}

void work()
{
    sum[0] = 1;
    for(R i = 1; i <= n; i ++)
    {
        for(R j = 1; j <= k; j ++)
        {
            if(s[i] != -1 && s[i] != j) continue;
            g[i][j] = ad(g[i - 1][j], (s[i] == -1 || s[i] == j));           
            if(g[i][j] < len) f[i][j] = sum[i - 1];
            else up(f[i][j], ad(ad(sum[i - 1], -sum[i - len]), f[i - len][j]));
            up(sum[i], f[i][j]);
        }
    }
    printf("%d\n", sum[n]);
}

int main()
{
    freopen("in.in", "r", stdin);
    pre();
    work();
    fclose(stdin);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章