qwq

ABC364题解(D-G)

D

先对 \(a\) 从小到大排序。

将题目转化成找到最小的 \(d\),使得恰好有 \(k\)\(a_i\in [b-d,b+d]\)

对于每个询问 \(b,k\),考虑二分答案。

设待检查的答案为 \(d\),二分找到最小的 \(p1\) 使得 \(a_{p1}\geq b-d\) 和最小的 \(p2\) 使得 \(a_{p2}> b+d\),包含的数的个数即为 \(p2-p1\)

  • 如果 \(p2-p1\ge k\)\(r\gets mid\)
  • 否则,\(l\gets mid+1\)

答案即为 \(r\)

时间复杂度 \(O(n\log^2n)\)

E

考虑 dp 求解。

\(f(i,j,k)\) 表示 dp 到第 \(i\) 道菜,吃了 \(j\) 盘,总甜度为 \(k\) 时总咸度的最小值。

于是有转移:

\(f(i,j,k)=\min\left(f(i-1,j,k),f(i-1,j-1,k-a_i)+b_i\right)\)

答案是多少?

注意到最后一道菜随便吃,所以找到最大的 \(j\),使得存在 \(k\in[0,x]\) 满足 \(f(n,j,k)\leq y\)(先满足要求),答案即为 \(\min(j+1,n)\)(最后随便吃)。

如果找不到,答案就是 \(0\)

复杂度 \(O(n^2V)\)

F

考虑 kruskal 算法求最小生成树的思想:贪心从小到大选边,如果一条边两端在同一连通块内就不加入图中,否则加入。

注意到 \(n+i\) 号点相当于联通了 \([l_i,r_i]\) 中的所有点,但是暴力加边不可取,可以考虑并查集维护连通块。

具体的,\(n\) 个点所属并查集的根节点代表连通块右端点,合并两个连通块即为把 左侧连通块根节点的父亲 变为 右侧连通块的根节点。

这样我们就可以快速跳过连通块中间部分,快速找到两个连通块的连接处,然后加边。

所以把 \(>n\) 的点按照 \(c_i\) 排序,连接时从 \(l_i\) 所属连通块开始连接,直到 \(r_i\) 和所属连通块联通即可。答案即为连接的连通块数量。

复杂度线性。


厉害做法:

注意到上面并查集的作用实际上是快速找到连通块右端点并删除。有没有一种做法,也能快速找到右端点?

注意到要维护的内容很简单,如果用线段树,1 个 bool 就能维护子树里是否有值(也就是“1”)。

如果要维护的这么简单,不如直接在父节点维护底下每一个儿子是否有值。

bool 太浪费了,用一个 bit 维护就行了。

都用 bit 维护了,于是直接维护一个 ull,这样就能一次性维护高达 64 个儿子的状态了!

因为可以 \(O(1)\) 定位叶子的位置,所以可以从叶子开始向上找,如果同一块内有,再向下找,一次查找只要 \(O(\log_{64}n)\)

img

复杂度变成了 \(O(n\log_{64} n)\),和并查集跑得一样快!

单点修改类似不用 pushup 的线段树。

G

【模板】最小斯坦纳树

\(k\) 这么小,直接考虑状压 dp。

\(f(i,s)\) 表示考虑到点 \(i\)\(i\) 和集合 \(s\) 中的点通过修过的路联通的最小代价。

于是有转移:

\(f(i,s)=\min_{(i,j)\in edge}f(j,u)+w(i,j)\)

\(f(i,s)=\min_{u\subseteq s}\left(f(i,u)+f(i,s-u)\right)\)

注意到 \(f(i,s)\) 依赖于 \(s\) 的子集,所以先从小到大枚举 \(s\),再枚举 \(i\)

但是转移方成立有同层(同样的\(s\))之间的转移?

注意到第一个式子的转移像是最短路,可以用最短路进行同层的转移。

复杂度 \(O(n3^n+m2^n\log m)\)


D

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1e5 + 5;
int a[N], n, q;

int chk(int b, int k, int d)
{
    int p1 = lower_bound(a + 1, a + n + 1, b - d) - a;
    int p2 = upper_bound(a + 1, a + n + 1, b + d) - a - 1;
    return p2 - p1 + 1;
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    sort(a + 1, a + n + 1);
    while(q --)
    {
        int b, k; cin >> b >> k;
        int l = 0, r = 2e8;
        while(l < r)
        {
            int mid = l + r >> 1;
            if(chk(b, k, mid) >= k) r = mid;
            else l = mid + 1;
        }
        cout << r << "\n";
    }

    return 0;
}

E

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 85, M = 1e4 + 5;
ll f[N][N][M], n, a[N], b[N], x, y;

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> x >> y;
    for(int i = 1; i <= n; i ++) cin >> a[i] >> b[i];
    memset(f, 0x3f, sizeof f);
    f[0][0][0] = 0;
    for(int i = 1; i <= n; i ++)
    {
        for(int j = 0; j <= i; j ++)
        {
            for(int k = 0; k <= x; k ++)
            {
                f[i][j][k] = min(f[i][j][k], f[i - 1][j][k]);
                if(k - a[i] >= 0 && j && f[i - 1][j - 1][k - a[i]] + b[i] <= y) f[i][j][k] = min(f[i][j][k], f[i - 1][j - 1][k - a[i]] + b[i]);
            }
        }
    }
    for(int i = n; i >= 0; i --)
    {
        for(int j = 0; j <= x; j ++)
        {
            if(f[n][i][j] <= y)
                return cout << min(n, (ll)i + 1) << "\n", 0;
        }
    }
    cout << 0;

    return 0;
}

F(并查集)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5 + 5;
int n, q, fa[N];
struct node {int l, r, v;} a[N];

int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= n; i ++) fa[i] = i;
    for(int i = 1; i <= q; i ++) cin >> a[i].l >> a[i].r >> a[i].v;
    sort(a + 1, a + q + 1, [](node &x, node &y) {return x.v < y.v;});
    ll ans = 0;
    for(int i = 1; i <= q; i ++)
    {
        int u = find(a[i].l), ed = find(a[i].r);
        ans += a[i].v;
        while(u != ed) fa[u] = find(u + 1), u = find(u), ans += a[i].v;
    }
    if(find(1) != n) cout << -1, 0;
    else cout << ans;

    return 0;
}

F 厉害做法:\(O(n\log_{64}n)\)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 2e5 + 5;
int n, q;
struct node {int l, r, v;} a[N];

using ull = unsigned long long;
struct FastSet
{
    static const int B = 64;
    vector<vector<ull>> a;
    int n, h;
    FastSet(int __n)
    {
        n = __n;
        while(__n > 1)
        {
            __n = (__n + B - 1) / B, h ++;
            a.emplace_back(__n);
        }
    }
    inline void set(int pos)
    {
        for(int i = 0; i < h; i ++)
        {
            a[i][pos / B] |= (1ull << pos % B);
            pos /= B;
        }
    }
    inline void reset(int pos)
    {
        for(int i = 0; i < h; i ++)
        {
            a[i][pos / B] &= ~(1ull << pos % B);
            if(a[i][pos / B]) return;
            pos /= B;
        }
    }
    int find_next(int pos)
    {
        for(int i = 0; i < h; i++)
        {
            ull d = (a[i][pos / B] >> (pos % B) + 1);
            if(pos % B != 63 && d)
            {
                pos += __builtin_ctzll(d) + 1;
                for(int j = i - 1; j >= 0; j--)
                {
                    pos *= B;
                    pos += __builtin_ctzll(a[j][pos / B]);
                }
                return pos;
            }
            pos /= B;
        }
        return n;
    }
};

int main()
{
    ios_base::sync_with_stdio(false);cin.tie(0);
    cin >> n >> q;
    for(int i = 1; i <= q; i ++) cin >> a[i].l >> a[i].r >> a[i].v;
    sort(a + 1, a + q + 1, [](node &x, node &y) {return x.v < y.v;});
    FastSet st(n + 1);
    for(int i = 1; i <= n; i ++) st.set(i);
    ll re = 0;
    for(int i = 1; i <= q; i ++)
    {
        int l = a[i].l, r = a[i].r, c = a[i].v;
        l --;
        int cnt = 0;
        while(1)
        {
            int v = st.find_next(l);
            if(v >= r) break;
            st.reset(v);
            ++cnt;
        }
        re += 1ll * c * (1 + cnt);
    }
    cout << (st.find_next(0) == n ? re : -1);
    return 0;
}


G

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 4e3 + 5, M = 1.6e4 + 5, K = 9;
ll f[N][(1 << K) + 5];
int n, m, k;
int fst[N], nxt[M], to[M], w[M], h = 2;
void add(int x, int y, int z)
{
    nxt[h] = fst[x], fst[x] = h, to[h] = y, w[h] = z, h ++;
}

void spread(int j)
{
    static bool inq[N];
    queue<int> q;
    for(int i = 1; i <= n; i ++) q.push(i), inq[i] = 1;
    while(q.size())
    {
        auto t = q.front(); q.pop();
        inq[t] = 0;
        for(int o = fst[t]; o; o = nxt[o])
        {
            int i = to[o], w = ::w[o];
            if(f[i][j] > f[t][j] + w)
            {
                f[i][j] = f[t][j] + w;
                if(!inq[i]) q.push(i), inq[i] = 1;
            }
        }
    }
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> m >> k; k --;
    for(int i = 1; i <= m; i ++)
    {
        int x, y, z; cin >> x >> y >> z;
        add(x, y, z);
        add(y, x, z);
    }
    memset(f, 0x3e, sizeof f);
    for(int i = 0; i < k; i ++)
        f[i + 1][1 << i] = 0;
    for(int j = 0; j < (1 << k); j ++)
    {
        for(int i = 1; i <= n; i ++)
        for(int s = j; s > (j ^ s); s = (s - 1) & j)
            f[i][j] = min(f[i][j], f[i][s] + f[i][j ^ s]);
        spread(j);
    }
    for(int i = k + 1; i <= n; i ++) cout << f[i][(1 << k) - 1] << "\n";

    return 0;
}

作者:adam01

出处:https://www.cnblogs.com/adam01/p/18327669

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   adam01  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题