每日构造/DP(4.25)

D. Cyclic Rotation

听说很多人赛时都被这道题卡了。。感觉并不难想啊。。

逆向思考,原题意相当于如果存在 \(b[i] = b[i-1]\) ,则 \(b[i]\) 可以放在前方任意一个位置,然后这题就做完了。。

具体做法的话先统计 \(b\) 数组中哪些数可以放前排去,然后开双指针,如果 \(l\)\(r\) 指向的当前位匹配不上,就把刚刚统计的数中与 \(a[l]\) 相匹配且位置靠前的数放在 \(r\) 指向的位置,可以证明这样的贪心是正确的。。然后如果出现某个位置既不匹配又没数可放就寄了。。

#include <bits/stdc++.h>
#define IOS                           \
    std::ios::sync_with_stdio(false); \
    std::cin.tie(0);                  \
    std::cout.tie(0);

bool solve()
{
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1), b(n + 1), vis(n + 1);
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i];
    for (int i = 1; i <= n; ++i)
        std::cin >> b[i];
    std::queue<int> q[n + 1];
    for (int i = 1; i <= n; ++i)
        if (b[i] == b[i - 1])
            q[b[i]].push(i);
    int l = 1, r = 1, delta = 0; // delta记录从后面拿了多少个数放前面,每放一个,后面的数相当于往后挪了一位
    while (l <= n && r <= n)
    {
        while (vis[r])
            ++r;
        if (a[l] == b[r])
            vis[r] = 1, ++l, ++r;
        else
        {
            if (q[a[l]].empty())
                return false;
            int now = q[a[l]].front();
            while (vis[now])
            {
                q[a[l]].pop();
                if (q[a[l]].empty())
                    return false;
                now = q[a[l]].front();
            }
            if (now + delta > l)
            {
                vis[now] = 1;
                ++l, ++delta;
            }
            else
                return false;
        }
    }
    return true;
}

int main()
{
    IOS;
    int t;
    std::cin >> t;
    while (t--)
        std::cout << (solve() ? "YES" : "NO") << std::endl;
    return 0;
}

E - RLE

\(f[i][j]\) 表示 \(|S| = i,|T| = j\) 的方案数,有转移方程:

\(f[i+k][j+c(k)+1] += 25*f[i][j]\)\(c(k)\) 表示数字 \(k\) 的位数

复杂度为 \(O(n^3)\) ,考虑优化,由于 \(f[i][j]\) 对相同位数的 \(k\) 作的贡献是相同的,因此想到区间加

区间加可以用什么实现呢?差分、前缀和、树状数组等等均可。这里用的是差分, \(d[i][j] = f[i][j] - f[i-1][j]\)

#include <bits/stdc++.h>
#define IOS                           \
    std::ios::sync_with_stdio(false); \
    std::cin.tie(0);                  \
    std::cout.tie(0);
using ll = long long;

int main()
{
    IOS;
    int n, p;
    std::cin >> n >> p;
    std::vector<std::vector<ll>> f(n + 1, std::vector<ll>(n + 1));
    std::vector<std::vector<ll>> d(n + 2, std::vector<ll>(n + 1));
    auto calc = [&](int x)
    {
        int res = 1;
        while (x)
            x /= 10, ++res;
        return res;
    };
    std::vector<int> ten = {0, 1, 10, 100, 1000, 10000};
    std::vector<int> len(n + 1);
    for (int i = 1; i <= n; ++i)
        len[i] = calc(i);
    for (int i = 1; i <= n; ++i)
        d[i][len[i]] += 26, d[i + 1][len[i]] -= 26;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = len[i]; j <= n; ++j)
        {
            f[i][j] = (d[i][j] + f[i - 1][j]) % p;
            for (int k = 1; i + ten[k] <= n && j + k + 1 <= n; ++k)
            {
                d[i + ten[k]][j + k + 1] = (d[i + ten[k]][j + k + 1] + 25 * f[i][j]) % p;
                if (i + ten[k + 1] <= n)
                    d[i + ten[k + 1]][j + k + 1] = ((d[i + ten[k + 1]][j + k + 1] - 25 * f[i][j]) % p + p) % p;
            }
        }
    }
    ll ans = 0;
    for (int i = 1; i < n; ++i)
        ans = (ans + f[n][i]) % p;
    std::cout << ans;
    return 0;
}
posted @ 2022-04-26 22:01  FoXreign  阅读(15)  评论(0编辑  收藏  举报