板刷2023.10.04

发布时间 2023-10-06 12:49:35作者: Zeoy_kkk

CF1878 F.Vasilije Loves Number Theory

image-20231006121613935

题解:约数个数 + 取模性质

  • \(n\)质因子分解得到,\(n =p_1^{\alpha_1}p_2^{\alpha_2}...p_k^{\alpha_k}\)
  • 那么显然\(d(n) = (\alpha_1 + 1)\times (\alpha_2 + 1) ... (\alpha_k + 1)\)
  • 根据题意可以得到:\(n \% d(n) = 0\)的时候一定存在一个正整数\(a\)使得题目要求成立
  • 那么现在的问题在于怎么判断\(n \% d(n) = 0\),因为\(n\)可能很大,所以我们不妨利用取模的性质:$a \times b % p = a % p \times b % p $
  • 所以我们只需要用\(map\)记录\(n\)中质因子及其幂次,然后快速幂即可得到\(n\)\(d(n)\)取模后的值
int n, q;
map<int, int> mp, mp1;

ll qpow(ll a, ll b, ll p)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = (__int128)res * a % p;
        a = (__int128)a * a % p;
        b >>= 1;
    }
    return res;
}
void solve()
{
    cin >> n >> q;
    mp.clear(), mp1.clear();
    for (int i = 2; i <= n / i; ++i)
    {
        while (n % i == 0)
        {
            mp[i]++;
            n /= i;
        }
    }
    if (n > 1)
        mp[n]++;
    mp1 = mp;
    while (q--)
    {
        int op, x;
        cin >> op;
        if (op == 1)
        {
            cin >> x;
            for (int i = 2; i <= x / i; ++i)
            {
                while (x % i == 0)
                {
                    mp1[i]++;
                    x /= i;
                }
            }
            if (x > 1)
                mp1[x]++;
            int sum = 1;
            for (auto [a, b] : mp1)
                sum *= (b + 1);
            int r = 1;
            for (auto [a, b] : mp1)
                r = r * qpow(a, b, sum) % sum;
            if (r % sum == 0)
                cout << "YES" << endl;
            else
                cout << "NO" << endl;
        }
        else
            mp1 = mp;
    }
    cout << endl;
}

CF1851 G. Vlad and the Mountains

image-20231006122635599

题解:离线 + 并查集

  • 我们观察性质:如果我们从\(i\)移动到\(j\),代价为\(h[j] - h[i]\),然后我们再从\(j\)移动到\(k\),代价为\(h[k] - h[j]\),所以易得从\(i\)移动到\(k\),代价为\(h[k] - h[i]\)
  • 所以,容易证明从任意一个点\(i\)移动到任意另一个点\(j\),代价为\(h[j] - h[i]\)
  • 那么题目就转化为:从\(a\)点移动到\(b\)点,且经过的点的点权必须\(\leq h_a + e\)
  • 这是一个经典的问题
  • 显然我们可以将询问离线后进行排序,然后利用并查集判断即可
int n, m, q, fa[N], a[N];
array<int, 2> h[N];
vector<int> g[N];

int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if (fx != fy)
        fa[fx] = fy;
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        fa[i] = i, g[i].clear();
    for (int i = 1; i <= n; ++i)
    {
        cin >> h[i][0];
        h[i][1] = i;
        a[i] = h[i][0];
    }
    for (int i = 1; i <= m; ++i)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cin >> q;
    vector<array<int, 4>> qry;
    for (int i = 1; i <= q; ++i)
    {
        int u, v, e;
        cin >> u >> v >> e;
        qry.push_back({h[u][0] + e, u, v, i});
    }
    sort(h + 1, h + n + 1);
    queue<array<int, 2>> que;
    for (int i = 1; i <= n; ++i)
        que.push(h[i]);
    sort(all(qry));
    vector<int> ans(q + 10);
    for (auto [x, u, v, qid] : qry)
    {
        // cout << x << " " << u << " " << v << " " << qid << endl;
        while (que.size() && que.front()[0] <= x)
        {
            int u = que.front()[1];
            que.pop();
            for (auto v : g[u])
            {
                if (a[v] <= x)
                    merge(u, v);
            }
        }
        if (find(u) == find(v))
            ans[qid] = 1;
        else
            ans[qid] = 0;
    }
    for (int i = 1; i <= q; ++i)
    {
        if (ans[i])
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    cout << endl;
}

CF1841 D. Pairs of Segments

image-20231006123428481

\(2 \leq n \leq 2000\)

题解:最大不相交区间数

  • 显然我们可以\(O(n^2)\)将所有的相交的线段合并成一个线段
  • 然后就是一个经典的问题:在一些区间中,求最大不相交区间个数
  • 显然按照右端点排序后遍历一遍即可
int n;
array<int, 2> seg1[N], seg[M];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        int l, r;
        cin >> l >> r;
        seg1[i] = {l, r};
    }
    int idx = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = i + 1; j <= n; ++j)
        {
            auto [l1, r1] = seg1[i];
            auto [l2, r2] = seg1[j];
            if (r2 >= l1 && l2 <= r1)
                seg[++idx] = {max(r1, r2), min(l1, l2)};
        }
    sort(seg + 1, seg + idx + 1);
    int now = 0, sum = 0;
    for (int i = 1; i <= idx; ++i)
    {
        auto [r, l] = seg[i];
        if (i == 1)
            now = r, sum++;
        else if (l > now)
            now = r, sum++;
    }
    cout << n - 2 * sum << endl;
}