Codeforces Round #548 (Div. 2) 解题报告 (CF1139)

A – Even Substrings

很简单,扫描的时候如果所在数位为偶数位,向答案添加当前数位即可。

// A.cpp
#include <bits/stdc++.h>
using namespace std;
char str[66000];
int main()
{
    int ans = 0, len;
    scanf("%d", &len), scanf("%s", str + 1);
    for (int i = 1; i <= len; i++)
        if (((str[i] - '0') & 1) == 0)
            ans += i;
    printf("%d", ans);
    return 0;
}

B – Chocolates

从后面往前扫描,取上一个值减一和限制的最小值即可(记得特判\(0\)的情况)。

// B.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = (int)2e5 + 2000;
int n, arr[MAX_N], trial[MAX_N];
ll check()
{
    memset(trial, 0x3f, sizeof(trial));
    ll acc = 0;
    for (int i = n; i >= 1; i--)
    {
        if (trial[i + 1] == 0)
        {
            trial[i] = 0;
            continue;
        }
        trial[i] = min(trial[i + 1] - 1, arr[i]);
        acc += trial[i];
    }
    return acc;
}
int main()
{
    ll l = 0, r = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &arr[i]), r += arr[i];
    printf("%lld", check());
    return 0;
}

C – Edgy Trees

乍一看很难的题,其实很水。

考虑直接容斥掉,初始答案为\(n^k\),在建树的时候忽略掉黑边,然后对于每一个连通块计算出贡献\(subtreeSiz^k\),记得这个贡献是负的:

\[ans = n^k – \sum_{t \in subtree} t.size()^k\]

// C.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 1e5 + 2000, mod = 1e9 + 7;
int head[MAX_N], current, n, tmpx, tmpy, tmpz;
int subtreeSiz[MAX_N], totTree, k;
bool vis[MAX_N];
struct edge
{
    int to, nxt, color;
} edges[MAX_N];
void addpath(int src, int dst, int color)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].color = color, head[src] = current++;
}
ll quick_pow(ll bas, ll tim)
{
    if (bas == 1)
        return 1;
    ll ans = 1;
    while (tim)
    {
        if (tim & 1)
            ans = ans * bas % mod;
        bas = bas * bas % mod;
        tim >>= 1;
    }
    return ans;
}
void dfs(int u, int org, int fa)
{
    vis[u] = true;
    subtreeSiz[org]++;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            dfs(edges[i].to, org, u);
}
int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &k);
    bool flag = false;
    for (int i = 1; i <= n - 1; i++)
    {
        scanf("%d%d%d", &tmpx, &tmpy, &tmpz);
        if (tmpz != 1)
            addpath(tmpx, tmpy, tmpz), addpath(tmpy, tmpx, tmpz);
        else
            flag = true;
    }
    if (!flag)
        printf("0"), exit(0);
    for (int i = 1; i <= n; i++)
        if (!vis[i])
            dfs(i, ++totTree, 0);
    ll ans = quick_pow(n, k);
    for (int i = 1; i <= totTree; i++)
        ans = (ans - quick_pow(subtreeSiz[i], k) + mod) % mod;
    printf("%lld", ans);
    return 0;
}

D – Steps to One

参考了这位巨佬的文章:cf1139D. Steps to One(dp)

首先考虑设计状态,设\(dp[i]\)为当\(gcd\)为\(i\)时将整个序列的\(gcd\)变成\(1\)的期望长度。这个状态可以搞出:

\[ dp[i] = \frac{ \sum_{j = 1}^i dp[gcd(i,j)]+1 }{m} \]

然后我们可以枚举\(k=gcd(i,j)\):

\[ dp[i] = 1 + \frac{ \sum_{k = 1}^m dp[k]*f(i,k) }{m} \]

其中\(f(i,k)\)代表在\(j \in [1,m]\)区间内满足\(gcd(i,j)=k\)的\(j\)的个数,我们可以考虑进行预处理。注意到枚举时可能会枚举到\(i\)自己,所以提出来:

\[ (1-\frac{\lfloor \frac{m}{i} \rfloor}{m})dp[i] = 1 + \frac{\sum_{k=1}^{i-1} dp[k]*f(i,k)}{m} \]

我们现在来思考如何预处理\(f\)函数。首先,我们可以把初值都设置为\(f(i,k) = \lfloor \frac{m}{k} \rfloor\),然后对于\(u>v,v|u\)的数对,\(f(i,v)-=f(i,u)\)进行容斥即可。

// D.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 1e5 + 200, mod = 1e9 + 7;
vector<int> facts[MAX_N], cnt[MAX_N];
ll m, dp[MAX_N], invm;
ll quick_pow(ll bas, ll tim)
{
    ll ans = 1;
    while (tim)
    {
        if (tim & 1)
            ans = ans * bas % mod;
        bas = bas * bas % mod;
        tim >>= 1;
    }
    return ans;
}
int main()
{
    scanf("%lld", &m), invm = quick_pow(m, mod - 2);
    for (int i = 1; i <= m; i++)
        for (int j = i; j <= m; j += i)
            facts[j].push_back(i);
    for (int i = 1; i <= m; i++)
    {
        cnt[i].resize(facts[i].size() + 1);
        for (int j = facts[i].size() - 1; j != -1; j--)
        {
            cnt[i][j] = m / facts[i][j];
            int siz = facts[i].size();
            for (int k = j + 1; k < siz; k++)
                if (facts[i][k] % facts[i][j] == 0)
                    cnt[i][j] -= cnt[i][k];
        }
    }
    ll ans = 0;
    for (int i = 2; i <= m; i++)
    {
        int dom = m, tmp = 0;
        for (int j = 0; j < facts[i].size(); j++)
            if (facts[i][j] == i)
                dom -= cnt[i][j];
            else
                tmp = (tmp + cnt[i][j] * dp[facts[i][j]] + mod) % mod;
        dp[i] = (tmp + m + mod) % mod * quick_pow(dom, mod - 2) % mod;
    }
    for (int i = 1; i <= m; i++)
        ans = (ans + dp[i] + 1 + mod) % mod;
    printf("%lld", ans * invm % mod);
    return 0;
}

Leave a Reply

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