2025.7.22 Educational Codeforces Round 181 (Rated for Div. 2)
Solved: 5/6
A. Difficult Contest
题意
给一个字符串,重排使得其不存在子串 FFT 和 NTT。
做法
注意到按字典序从大到小排列即满足要求。
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\)。则我们必须保证
其中 \(c_1,\dots,c_n\) 是正整数。注意到当 \(s = y_n+1\) 时,\(c_n\) 的系数为 1,因此只要
就一定存在一组满足条件的 \(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\) 时是否有解,即
是否成立。
枚举 \(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)\),则答案为
预处理划分数及其前缀和,总的时间复杂度为 \(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';
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号