Logo __vector__ 的博客

博客

CF1811G2 题解

...
__vector__
2025-12-01 12:55:52

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-04-09 12:10:21

纪念一下第一次不看题解,不看算法标签做出 CF 评分 2200。
虽然实际上最多提高组 T1。

G1 做法

G2 做法只需要在 G1 基础上稍微修改。

很自然想到设 $dp_{i,j}$ 表示第 $i$ 位是路径的第 $j$ 个点的方案数。

想一下如何转移,对于 $i = 1$,显然方案数是 $1$。
对于 $i \ge 2$,分一下类:

  1. 若 $i \equiv 1 \pmod k$,那么对于所有 $j \le i-1$,从 $dp_{j,i-1}$ 转移,颜色不用管。
  2. 否则,还要在情况 1 的基础上加上一条限制:第 $i$ 个点与 $j$ 点颜色相同。

主要思想就是这个。

关于怎么统计之前符合某些条件的状态答案综合,这个开个数组记一下就行了,都把 dp 设计出来了,这么简单的想必都会。

剩下的就是从大到小枚举 $m$,每次跑一遍 dp,找到答案就终止。

dp 复杂度 $O(n^2)$。

枚举最劣复杂度 $O(n)$。

总复杂度 $O(n^3)$。

G2 做法

dp 不大可能优化了,看枚举过程很方便优化。

主要就是优化寻找最长路径长度的过程。

我们可以先做一遍 dp,看路径长度最高到多少,使得答案不为 $0$。

然后对这个路径长度 dp 一下就行了。

UPD:做法麻烦了,实际上直接 dp 一遍就行了。

Code

\/\/ LUOGU_RID: 107120197
#include <bits\/stdc++.h>
using namespace std;
#define FOR(i, a, b) for (int i = a; i <= b; i++)
#define REP(i, a, b) for (int i = a; i >= b; i--)
#define pb push_back()
#define mkpr make_pair
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int maxn = 5005;
typedef long long ll;
const ll mod = 1e9 + 7;
int t;
int n, k;
int c[maxn];
ll dp[maxn][maxn];  \/\/ 第 i 位状态为 j
ll sum[maxn][maxn]; \/\/ 颜色为 i,状态为 j,dp 值和
ll sum2[maxn];      \/\/ 状态为 i dp 总和
bool dp2[maxn][maxn],sum21[maxn][maxn],sum22[maxn];
inline int turn(int status)
{
    if (status % k == 0)
        return k;
    return status % k;
}
int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n, &k);
        FOR(i, 1, n)
        {
            scanf("%d", &c[i]);
        }
        ll ans = 0;
        FOR(i, 1, n)
        {
            dp2[i][1]=1;
            FOR(j, 2, n)
            {
                if (turn(j) == 1)
                {
                    dp2[i][j]|=sum22[j-1];
                }
                else
                {
                    dp2[i][j]|=sum21[c[i]][j-1];
                }
            }
            FOR(j, 1, n)
            {
                sum22[j] |= dp2[i][j];
                sum21[c[i]][j] |= dp2[i][j];
            }
        }
        int blocks = n \/ k;
        bool isansoutputed=0;
        for (int m = blocks * k; m > 0; m -= k)
        {
            bool flag = 0;
            FOR(i, 1, n)
            {
                if (dp2[i][m])
                {
                    flag = 1;
                    break;
                }
            }
            if (flag)
            {
             \/\/   printf("m = %d\n", m);
                FOR(i, 1, n)
                {
                    dp[i][1] = 1;
                    FOR(j, 2, m)
                    {
                        if (turn(j) == 1)
                        {
                            dp[i][j] += sum2[j - 1];
                        }
                        else
                        {
                            dp[i][j] += sum[c[i]][j - 1];
                        }
                        dp[i][j] %= mod;
                    }
                    FOR(j, 1, m)
                    {
                        sum2[j] += dp[i][j];
                        sum2[j]%=mod;
                        sum[c[i]][j] += dp[i][j];
                        sum[c[i]][j] %= mod;
                    }
                }
                FOR(i, 1, n)
                {
                    ans += dp[i][m];
                    ans%=mod;
                    FOR(j, 1, n)
                    {
                        \/\/    printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
                        dp[i][j] = 0;
                        sum[i][j] = 0;
                    }
                    sum2[i] = 0;
                }
                isansoutputed=1;
                break;
            }
        }
        if (!isansoutputed)
            ans++;
        printf("%lld\n", ans % mod);
        FOR(i, 1, n)
        {
            sum2[i] = sum22[i]=0;
            FOR(j, 1, n)
            {
                dp[i][j] = dp2[i][j]=0;
                sum[i][j] = sum21[i][j]=0;
            }
        }
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。