D

学过筛的很容易发现 (有彩笔一开始没发现写了个三log做法,我不说是谁) ,这实际上就是个筛的过程。

输出最小的没出现的质数即可,显然可以双指针,当然也可以二分。

std::vector<int> ps, phi, mu;
void getPrime(int n = 3e6)
{
    phi.resize(n+1); mu.resize(n+1);
    std::vector<bool> vis(n+1, false);
    vis[1] = true; phi[1] = 1; mu[1] = 1;
    for(int i = 2; i <= n; ++i)
    {
        if(!vis[i]) ps.push_back(i), phi[i] = i-1, mu[i] = -1;
        for(auto p: ps)
        {
            if(1ll * i * p > n) break;
            vis[i*p] = true;
            if(!(i%p))
            {   
                mu[i*p] = 0;
                phi[i*p] = phi[i] * p; 
                break; 
            }
            phi[i*p] = phi[i] * phi[p];
            mu[i*p] = -mu[i];
        }
    }
}
void solve()
{
    int n;
    std::cin >> n;
    std::vector<int> a(n+1);
    for(int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
    std::ranges::sort(a);
    for(int i = 0, j = 1; i < (int)ps.size() && j < (int)a.size(); ++i) {
        while(j < (int)a.size() && a[j] < ps[i]) ++j;
        if(j > n || a[j] != ps[i]) {
            std::cout << ps[i] << '\n';
            return;
        }
    }
}

E

补个并查集暴力合并并维护是否可以继续向后合并。每个点只会被合并一次,复杂度

最后把前 大的连通块统计出来即可。

void solve()
{
    int n, m;
    std::cin >> n >> m;
    std::vector<pii> a(n+1);
    for(int i = 1; i <= n; ++i) {
        std::cin >> a[i].second;
    }
    for(int i = 1; i <= n; ++i) {
        std::cin >> a[i].first;
    }
    std::ranges::sort(a);
    Dsu dsu(n);
    std::vector<int> sz;
    for(int i = 1; i <= n; ++i) {
        if(dsu.size(i) != 1) continue;
        int r = a[i].first + a[i].second;
        int cur = i;
        while(1) {
            int p = std::ranges::upper_bound(a, pii(r, 2e9)) - a.begin() - 1;
            if(p == cur) break;
            for(int j = cur + 1; j <= p; ++j) {
                chkmax(r, a[j].first + a[j].second);
                dsu.merge(i, j);
            }
            cur = p;
        }
        sz.push_back(dsu.size(i));
    }
    std::ranges::sort(sz, std::greater<>());
    int ans = 0;
    for(int i = 0; i < m && i < int(sz.size()); ++i) {
        ans += sz[i];
    }
    std::cout << ans << '\n';
}

F

答案对应 , 为所有墙的区间长度。

首先发现只有相邻两项的距离有用(更远的可以逐个用相邻两项拼出来)。然后发现相同长度的只要一个,于是直接区间长度数量从 退化到

随后就是一个 完全背包,随便做做。

void solve()
{
    int n, m, t;
    std::cin >> n >> m >> t;
    t -= n;
    std::vector<int> x(m + 1);
    for(int i = 1; i <= m; ++i) {
        std::cin >> x[i];
    }
    if(t < 0) {
        std::cout << "0\n";
        return;
    }
    std::ranges::sort(x);
    x.erase(std::unique(x.begin(), x.end()), x.end());
    m = x.size() - 1;
    std::vector<int> dx(m);
    for(int i = 2; i <= m; ++i) {
        dx[i - 1] = x[i] - x[i-1];
    }
    std::ranges::sort(dx);
    dx.erase(std::unique(dx.begin(), dx.end()), dx.end());
    int tt = t / 2;
    std::vector<bool> dp(tt + 1); 
    dp[0] = true;
    for(int i = 1; i < int(dx.size()); ++i) {
        for(int j = dx[i]; j <= tt; ++j) {
            dp[j] = dp[j] | dp[j - dx[i]];
        }
    }
    int mx = 0;
    for(int i = tt; i >= 0; --i) {
        if(dp[i]) {
            mx = i;
            break;
        }
    }
    std::cout << mx * 2 + n << '\n';
}

G

跟965的E很相似的一个东西。

公式化区间转端点,然后每个点内统计答案的时候可以直接暴力向上合并。

为什么呢?

因为除了第一次是合并 的鱼, 每次合并的一定都在 的范围内, 这样只要有新值进来就能直接使答案倍增,因此暴力合并的次数是 的。

挂个线段树就是 。当然能有其他东西挂过来,但是线段树确实足够无脑。

void solve()
{
    int n, x;
    std::cin >> n >> x;
    std::vector<std::array<int, 3>> a(n+1);
    std::vector<int> b{0}, c{0, x};
    for(int i = 1; i <= n; ++i) {
        auto &[l, r, x] = a[i];
        std::cin >> l >> r >> x;
        b.push_back(l); b.push_back(r); c.push_back(x);
    }
    std::ranges::sort(b);
    b.erase(std::unique(b.begin(), b.end()), b.end());
    std::ranges::sort(c);
    c.erase(std::unique(c.begin(), c.end()), c.end());
    int m = b.size() - 1, t = c.size() - 1;
    std::vector<std::vector<int>> L(m + 1), R = L;
    for(int i = 1; i <= n; ++i) {
        auto &[l, r, x] = a[i];
        l = std::ranges::lower_bound(b, l) - b.begin();
        r = std::ranges::lower_bound(b, r) - b.begin();
        x = std::ranges::lower_bound(c, x) - c.begin();
        L[l].push_back(i); R[r].push_back(i);
    }
    i64 ans(0);
    SegmentTree<Info, Tag> st(t);
    for(int i = 1; i <= m; ++i) {
        for(auto id: R[i]) st.modify(a[id][2], -c[a[id][2]]);
        for(auto id: L[i]) st.modify(a[id][2], c[a[id][2]]);
        i64 sum = x;
        int lst = 1;
        while(1) {
            int cur = std::ranges::upper_bound(c, sum) - c.begin() - 1;
            auto tmp = st.query(lst, cur).sum;
            sum += tmp;
            lst = cur + 1;
            if(tmp == 0 || lst > t) break;
        }
        chkmax(ans, sum);
    }
    std::cout << ans << '\n';
}