题解:P11230 [CSP-J 2024] 接龙(民间数据)

· · 题解

Luogu P11230 [CSP-J 2024] 接龙 Solution

这(蓝)题真真有 S 组的难度了,但是放在 CSP-J T4 的位置来说也没有太超纲。

Update

第一更:2024.10.27 11:35

第二更:2024.10.30 16:35 【修改了引言的一点错误】

第三更:2024.11.9 11:35 【修改了民间数据的题解方法】

题目大意

给定 n 个人的数字序列,有 q 次询问,每次询问这 n 个人是否能完成 r 轮的接龙,最后一轮接龙的最后一个数字是 c,同时满足相关条件。

解法

每次询问恰好有 r 轮接龙且最后一个数字能否以 c 结尾,可以定义状态 dp[r][c] 验证是否满足。要求如果知道了第 r 轮的结果,则只需要将第 r 轮的最后一个数字作为下一轮的起点,那么就可以递推到 r+1 轮,故本题采用动态规划的方案。

Step 1 预处理

状态:dp[r][c] 恰好 r 轮接龙且最后一个数字以 c 结尾,n 表示人数,k 表示接龙子序列长度范围上限,q 表示询问次数。l[i] 表示第 i 个人数字序列的累计和,s[i] 存储数字序列中的数字,cnt[r][c] 表示恰好 r 轮接龙且最后一个数字以 c 结尾的方法数量。

Step 2 处理接龙

定义 \text{solve} 函数用于处理接龙问题。初始化 dpdp2cnt 数组为全 \text{false} 和全 \text{0}。输入人数 n 、接龙子序列长度上限 k 和询问次数 q

Step 3 调用函数

用一个 \text{while} 循环, 调用 \text{solve} 函数就好。

注意:本题数据规模较大,注意优化。

代码 【民间数据】

最后贴上code

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 10, R = 1e2 + 10;
bool dp[R][N];
bool dp2[R][N];

int n, k, q, l[N], s[N], cnt[R][N];

void solve(){
    memset(dp, 0, sizeof dp);    
    memset(dp2, 0, sizeof dp2);    
    memset(cnt, 0, sizeof cnt);
    cin >> n >> k >> q;
    for (int i = 1; i <= n; i++){
        cin >> l[i];
        l[i] += l[i - 1];
        for (int j = l[i - 1] + 1; j <= l[i]; j++) cin >> s[j];}
    for (int i = 1; i <= l[n]; i++) 
        if (s[i] == 1){
            dp[0][1] = dp2[0][i] = 1;
            cnt[0][1]++;}
    for (int r = 1; r <= 100; r++){
        for (int i = 1; i <= n; i++){
            for (int j = l[i - 1] + 1; j <= l[i]; j++)
                cnt[r - 1][s[j]] -= dp2[r - 1][j];
            int tot = 0;
            for (int j = l[i - 1] + 1; j <= l[i]; j++){
                if (j - k > l[i - 1])
                    tot -= (cnt[r - 1][s[j - k]] > 0);
                dp[r][s[j]] = dp[r][s[j]] || (tot > 0);
                if (tot > 0) dp2[r][j] = 1;
                cnt[r][s[j]] += dp2[r][j];
                tot += (cnt[r - 1][s[j]] > 0);}
            for (int j = l[i - 1] + 1; j <= l[i]; j++)
                cnt[r - 1][s[j]] += dp2[r - 1][j];}}
    while (q--){
        int r, c;
        cin >> r >> c;
        cout << dp[r][c] << endl;}}

signed main(){
    //freopen("chain.in", "r", stdin);
    //freopen("chain.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--)
        solve();

    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

注意:本题解仅适用于【民间数据】!!!

注意:将本题解代码复制到【官方数据】中只有5 \text{pts}