2025.7.22 Educational Codeforces Round 181 (Rated for Div. 2)

比赛链接

Solved: 5/6


A. Difficult Contest

题意

给一个字符串,重排使得其不存在子串 FFTNTT

做法

注意到按字典序从大到小排列即满足要求。

void solve(){
    string s;
    cin >> s;
    int n = s.length();
    sort(s.begin(), s.begin()+n, [=](char a, char b){ return a>b; });
    cout << s << '\n';
}

B. Left and Down

题意

\((0,0)\) 走到 \((a,b)\),每次可以移动 \((\Delta x, \Delta y), 0\leq \Delta x, \Delta y \leq k\),要最小化不同的移动步长数。\(1\leq a,b,k\leq 10^{18}\)

做法

最小的可以一次性走到终点的步长是 \((a/(a,b), b/(a,b))\)
因此如果 \(a/(a,b)\)\(b/(a,b)\) 都不大于 \(k\) 则答案为 1,否则答案为 2。

void solve(){
    ll a, b, k;
    cin >> a >> b >> k;
    ll d = gcd(a, b);
    a /= d, b /= d;
    if (a <= k && b <= k) cout << "1\n";
    else cout << "2\n";
}

C. Count Good Numbers

题意

\([l,r]\) 中质因子都 \(\geq 11\) 的数的个数。\(2\leq l\leq r\leq 10^{18}\)

做法

等价于求不整除 \(2,3,5,7\) 的数字个数。区间转前缀然后 \(2^4\) 容斥。

int d[16], o[16];
void init()
{
    d[0] = 1, d[1] = 2, d[2] = 3, d[4] = 5, d[8] = 7;
    o[0] = 1, o[1] = o[2] = o[4] = o[8] = -1;
    for (int i=0; i<16; ++i) if (!o[i]) o[i] = -o[i^(i&-i)], d[i] = d[i^(i&-i)] * d[i&-i];
}

ll calc(ll n)
{
    ll res = 0;
    for (int i=0; i<16; ++i) res += (n/d[i]) * o[i];
    return res;
}

void solve(){
    ll l, r;
    cin >> l >> r;
    cout << calc(r) - calc(l-1) << '\n';
}

D. Segments Covering

题意

\(n\) 条线段,第 \(i\) 条线段出现的概率为 \(p_i\),每条线段是否出现相互独立。问 \([1,m]\) 每个点恰好被一条线段覆盖的概率。\(1\leq n,m\leq 2\times 10^5\)

做法

dp。设 \(f_i\) 表示:仅考虑右端点不大于 \(i\) 的线段\([1,i]\) 每个点恰好被一条线段覆盖的概率。
则有转移 \(f_i = \sum_{[j,i]} q_{[j,i]}f_{j-1}\)。其中 \(q_{[j,i]}\) 是线段 \([j,i]\) 出现且当前考虑的线段中与它相交的都不出现的概率。

预处理右端点 \(\leq i\) 的线段的不出现概率的前缀积即可 \(O(\log p)\) 快速计算 \(q\)

const int N = 2e5+5;
vector <pii> v[N];
ll sq[N], f[N];
int n, m, l, r, x, y;
int main(){
    ios::sync_with_stdio(false);cin.tie(0);
    cin >> n >> m;
    for (int i=0; i<=m; ++i) v[i].clear(), sq[i] = 1;
    for (int i=1; i<=n; ++i)
    {
        cin >> l >> r >> x >> y;
        ll p = x * qpow(y) % mod;
        v[r].push_back({l, p});
    }
    for (int i=1; i<=m; ++i)
        for (auto [l, p] : v[i])
            sq[i] = sq[i] * (1 - p + mod) % mod;
    for (int i=1; i<=m; ++i) sq[i] = sq[i-1] * sq[i] % mod;
    f[0] = 1;
    for (int i=1; i<=m; ++i)
    {
        f[i] = 0;
        for (auto [l, p] : v[i])
        {
            f[i] = (f[i] + f[l-1] * p % mod * qpow(1 - p + mod) % mod * sq[i] % mod * qpow(sq[l-1])) % mod;
        }
    }
    cout << f[m] << '\n';
}

E. Sets of Complementary Sums

题意

如果一个集合 \(Q\) 可以通过如下方式构造:

  • 取一个长度为 \(m\geq 1\) 的正整数序列 \(\{x_i\}_{i=1}^m\)
  • \(s = \sum_{i=1}^m x_i\)\(Q = \{s-x_i | 1\leq i\leq m\}\)
  • \(Q\) 去重

则称 \(Q\) 是一个 set of complementary sums。现给定 \(n,x\),求大小为 \(n\),元素均在 \([1,x]\) 中的不同的 set of complementary sums 数量。

做法

考虑 \(Q = \{y_1,y_2,\dots,y_n\} (y_1<y_2<\dots<y_n)\) 是 set of complementary sums 的条件。
假设它是由序列 \(M\) 得到的,\(M\) 中包含 \(c_1\)\(s-y_1\)\(c_2\)\(s-y_2\),……,\(c_n\)\(s-y_n\)。则我们必须保证

\[s = \sum_{i=1}^n c_i(s-y_i), s > y_n \]

其中 \(c_1,\dots,c_n\) 是正整数。注意到当 \(s = y_n+1\) 时,\(c_n\) 的系数为 1,因此只要

\[\sum_{i=1}^n (s-y_i) \leq s, \]

就一定存在一组满足条件的 \(c_1,\dots,c_n\)(令 \(c_1,\dots,c_{n-1}\) 都为 1,剩下的用 \(c_n\) 来凑)。
另一方面,当 \(s > y_n+1\) 时,\(s\) 每增加 1 右端的系数和会增加 \(n\),故 \(s = y_n+1\) 时无解 \(s > y_n+1\) 时也必无解。综上可知我们只需判断 \(s = y_n+1\) 时是否有解,即

\[\sum_{i=1}^n (y_n+1-y_i) \leq y_n+1 \]

是否成立。

枚举 \(y_n\),再令 \(z_i = y_n - y_i, 1\leq i\leq n-1\),则不等式变为 \(\sum_{i=1}^{n-1} z_i \leq y_n-n+1\)。问题转化为求和不超过 \(y_n-n+1\),两两不同的无序 \(n-1\) 元组数量。我们令 \(z_i\)\(i-1\) 即可去掉“两两不同”这个限制,转化成了划分数模型。设划分数的列前缀和为 \(SP(n,m) = \sum_{i=1}^n P(i,m)\),则答案为

\[SP(y_n-n+1-\binom {n-1}2, n-1). \]

预处理划分数及其前缀和,总的时间复杂度为 \(O(nx)\)。虽然本题范围是 \(2\times 10^5\),但实际上由前面的推导可知 \(x< \frac{(n-1)(n+2)}2\) 时答案一定为 \(0\),故特判掉 0 的情况后复杂度降为 \(O(x\sqrt x)\)

const int N = 2e5+5;
int n, m, s[N];
vector <int> f[N];
void init(int n, int m)
{
    f[0].resize(m+1);
    for (int i=1; i<=n; ++i)
    {
        f[i].resize(m+1);
        f[i][1] = 1;
        for (int j=2; j<=i && j<=m; ++j)
            f[i][j] = (f[i-1][j-1] + f[i-j][j]) % mod;
    }
    for (int i=1; i<=n; ++i)
        s[i] = (s[i-1] + f[i][m]) % mod;
}

void solve()
{
    cin >> n >> m;
    if (n == 1)
    {
        cout << m << '\n';
        return;
    }
    if (n == 2)
    {
        cout << 1ll*m*(m-1)/2 % mod << '\n';
        return;
    }
    ll t = 1ll * (n-1) * (n+2) / 2;
    if (m < t)
    {
        cout << "0\n";
        return;
    }
    init(m - n*(n-1)/2, n-1);
    ll ans = 0;
    for (int i=t; i<=m; ++i)
    {
        ans = (ans + s[i - n*(n-1)/2]) % mod;
    }
    cout << ans << '\n';
}
posted @ 2025-07-23 01:26  EssnSlaryt  阅读(358)  评论(0)    收藏  举报