Codeforces 1574F 图论 + DP

题意

传送门 Codeforces 1574F Occurrences

题解

任一数组 A i A_i Ai 在构造数组中出现的次数不能小于 A i A_i Ai 中任意非空子数组的出现次数,那么存在重复元素的数组的任意非空子数组一定不会出现在构造串中。则可能出现的数组任意元素一定是互异的,而这样的数组可能存在重复元素,这样的重复部分仅当分别位于数组 A i A_i Ai 的后缀与 A j A_j Aj 的前缀才能出现在构造数组中。

对于任意出现在构造数组中的元素 A i , j A_{i,j} Ai,j,其后继一定是 A i , j + 1 A_{i,j+1} Ai,j+1;反之亦然。那么可以建图进行维护,前驱元素向后继元素连一条有向边,那么满足条件的连通分量是一条链。总节点数为 O ( K ) O(K) O(K),则不同链的长度数量为 O ( K ) O(\sqrt K) O(K )

d p [ i ] dp[i] dp[i] 代表长度为 i i i 的构造数组种类数,那么有 d p [ i ] = ∑ 1 ≤ j ≤ i d p [ i − j ] ∗ L [ j ] dp[i]=\sum_{1\leq j\leq i}dp[i-j]*L[j] dp[i]=1jidp[ij]L[j],其中 L [ j ] L[j] L[j] 为长度为 j j j 的链的数量。总时间复杂度 O ( M K ) O(M\sqrt K) O(MK )

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l, _ = r; i < _; ++i)
typedef long long ll;
const int MAXN = 3E5 + 5, MOD = 998244353;
int N, M, K, L[MAXN], dp[MAXN];
vector<int> pre[MAXN], nxt[MAXN];
bool bad[MAXN], used[MAXN];

int main()
{
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> N >> M >> K;
    rep(i, 0, N)
    {
    
    
        int c;
        cin >> c;
        vector<int> a(c);
        rep(i, 0, c) cin >> a[i], --a[i];
        if (set<int>(a.begin(), a.end()).size() != a.size())
        {
    
    
            for (auto &x : a)
                bad[x] = 1;
            continue;
        }
        rep(i, 0, c - 1)
        {
    
    
            int u = a[i], v = a[i + 1];
            nxt[u].push_back(v), pre[v].push_back(u);
        }
    }
    rep(i, 0, K)
    {
    
    
        sort(pre[i].begin(), pre[i].end());
        sort(nxt[i].begin(), nxt[i].end());
        pre[i].erase(unique(pre[i].begin(), pre[i].end()), pre[i].end());
        nxt[i].erase(unique(nxt[i].begin(), nxt[i].end()), nxt[i].end());
    }
    rep(i, 0, K)
    {
    
    
        if (used[i])
            continue;
        int cnt0 = 0, cnt2 = 0, n = 0;
        bool b = 0;
        queue<int> q;
        q.push(i);
        while (q.size())
        {
    
    
            ++n;
            int u = q.front();
            q.pop();
            used[u] = 1;
            b |= bad[u];
            cnt0 += (int)pre[u].size() == 0;
            cnt0 += (int)nxt[u].size() == 0;
            cnt2 += (int)pre[i].size() == 2;
            cnt2 += (int)nxt[i].size() == 2;
            for (auto &v : pre[u])
                if (!used[v])
                    q.push(v);
            for (auto &v : nxt[u])
                if (!used[v])
                    q.push(v);
        }
        if (!b && !cnt2 && cnt0 == 2)
            ++L[n];
    }
    vector<int> len;
    rep(i, 1, K + 1) if (L[i] > 0) len.push_back(i);
    dp[0] = 1;
    rep(i, 1, M + 1) for (auto &j : len) if (j <= i) dp[i] = (dp[i] + (ll)dp[i - j] * L[j]) % MOD;
    cout << dp[M] << '\n';
    return 0;
}

おすすめ

転載: blog.csdn.net/neweryyy/article/details/121557291