「FZOI」 Round #33 (Div. 0) – 解题报告

A – Function

在考场上只推出来 \(f(x) = \mu(x) * \sigma_0^2(x) = \prod_{i = 1}^m (2c_i + 1)\),显然用 min_25 就可以直接秒杀(但是我忘了板子呜呜呜呜呜)

其实我们都推理到这一步了,发现无非就是把 \(c_i\) 乘了个 \(2\)。所以其实 \(f(x) = \sigma_0(x^2)\)。这个其实推出了上面的那个 Dirichlet 卷积就已经清楚很多了。

现在考虑如何用快速的方法筛出 \(\sum_{x = 1}^n \sigma_0(x^2)\)。用 SPOJ DIVCNT2 的方法筛即可。

// function.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e6 + 500;

typedef long long ll;

int T, n, primes[MAX_N], tot;
ll sigma[MAX_N], mu[MAX_N], pmu[MAX_N];
bool vis[MAX_N];
unordered_map<ll, ll> ump;

ll getSigma(ll upper)
{
    if (upper < MAX_N)
        return sigma[upper];
    ll ret = 0;
    for (ll i = 1, gx; i <= upper; i = gx + 1)
    {
        gx = upper / (upper / i);
        ret += 1LL * (upper / i) * (gx - i + 1);
    }
    return ret;
}

ll getMu2(ll upper)
{
    if (upper < MAX_N)
        return pmu[upper];
    ll ret = 0;
    for (int i = 1; 1LL * i * i <= upper; i++)
        ret += 1LL * mu[i] * (upper / (1LL * i * i));
    return ret;
}

int main()
{
    sigma[1] = mu[1] = 1;
    for (int i = 2; i < MAX_N; i++)
    {
        if (!vis[i])
            primes[++tot] = i, mu[i] = -1, sigma[i] = 2;
        for (int j = 1; j <= tot && 1LL * i * primes[j] < MAX_N; j++)
        {
            vis[i * primes[j]] = true;
            if (i % primes[j] == 0)
            {
                mu[i * primes[j]] = 0;
                sigma[i * primes[j]] = 2LL * sigma[i] - sigma[i / primes[j]];
            }
            else
            {
                mu[i * primes[j]] = -mu[i];
                sigma[i * primes[j]] = sigma[i] * sigma[primes[j]];
            }
        }
    }
    for (int i = 1; i < MAX_N; i++)
        pmu[i] = pmu[i - 1] + mu[i] * mu[i], sigma[i] += sigma[i - 1];
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        ll ret = 0;
        for (int i = 1, gx; i <= n; i = gx + 1)
        {
            gx = n / (n / i);
            ret += getSigma(n / i) * (getMu2(gx) - getMu2(i - 1));
        }
        printf("%lld\n", ret);
    }
    return 0;
}

B – Permutation

打表发现,如果没确定的位置,答案就是 \(1 \times 3 \times 5 \dots\)。如果确定了,那就是 \(n!\)。

// permutation.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200, mod = 998244353;

int n, ai[MAX_N], deg[MAX_N];
bool vis[MAX_N];

bool dfs(int u)
{
    if (vis[u])
        return false;
    vis[u] = true;
    return ai[u] ? !dfs(ai[u]) : 0;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &ai[i]);
        if (ai[i] != 0)
            deg[ai[i]] = 1;
    }
    if (n & 1)
        puts("0"), exit(0);
    int x = 0, y = 0;
    for (int i = 1, tmp; i <= n; i++)
        if (!deg[i])
            tmp = dfs(i), y += tmp, x += !tmp;
    for (int i = 1; i <= n; i++)
        if (!vis[i] && dfs(i))
            puts("0"), exit(0);
    int ans = 1;
    for (int i = 1; i <= x; i += 2)
        ans = 1LL * ans * i % mod;
    ans = 1LL * ans * ans % mod;
    for (int i = x + 1; i <= x + y; i++)
        ans = 1LL * ans * i % mod;
    printf("%d\n", ans);
    return 0;
}

C – Subsequence

子序列自动上跑一个 DP,设计状态的时候要利用答案应该不大的性质:设 \(dp[i][j]\) 表示答案长度为 \(i\) 且到了 A 串自动机节点上 \(j\) 的最大的 B 串自动机节点。如果有 \(dp[i][a + 1] = b + 1\) (失配),那么答案就是 \(i\)。

// subsequence.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 4040;

int n, m, k, nxt[2][MAX_N][MAX_N], sA[MAX_N], sB[MAX_N], dp[MAX_N][MAX_N];

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
        scanf("%d", &sA[i]);
    for (int i = 1; i <= m; i++)
        scanf("%d", &sB[i]);
    for (int c = 1; c <= k; c++)
        nxt[0][n][c] = n + 1, nxt[1][m][c] = m + 1, nxt[0][n + 1][c] = n + 1, nxt[1][m + 1][c] = m + 1;
    for (int i = n - 1; i >= 0; i--)
    {
        for (int c = 1; c <= k; c++)
            nxt[0][i][c] = nxt[0][i + 1][c];
        nxt[0][i][sA[i + 1]] = i + 1;
    }
    for (int i = m - 1; i >= 0; i--)
    {
        for (int c = 1; c <= k; c++)
            nxt[1][i][c] = nxt[1][i + 1][c];
        nxt[1][i][sB[i + 1]] = i + 1;
    }
    memset(dp, -1, sizeof(dp)), dp[0][0] = 0;
    for (int i = 0; i <= min(n, m); i++)
    {
        for (int j = 0; j <= n; j++)
            if (dp[i][j] != -1)
            {
                for (int c = 1; c <= k; c++)
                    dp[i + 1][nxt[0][j][c]] = max(dp[i + 1][nxt[0][j][c]], nxt[1][dp[i][j]][c]);
            }
        if (dp[i + 1][n + 1] == m + 1)
            printf("%d\n", i + 1), exit(0);
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *