Loading

UNR#3 Day1

A. 鸽子固定器

把固定器按 \(s\) 排序。

如果选的个数 \(< m\),选出来的一定是一个连续段,否则再多选夹在中间的固定器一定优。

如果选的个数 \(= m\),按牢固度从小到大考虑每一个固定器,找以当前固定器为最小牢固度的长度为 \(m\) 的最优序列,分讨几种情况之后不难发现忽略牢固度更小的固定器后,这还是个连续段。

想清楚了暴力做就好,时间复杂度 \(\mathcal O(nm)\)

注意别用 vector,它的 erase 的时间复杂度是 \(\mathcal O(n)\)

推荐手写链表。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

constexpr int N = 2e5 + 10;

int n, m, ds, dv, pre[N], lat[N];

struct Node {int s, v, id;} a[N], b[N], c[N];
bool cmps(const Node &lhs, const Node &rhs) {return lhs.s < rhs.s;}
bool cmpv(const Node &lhs, const Node &rhs) {return lhs.v < rhs.v;}

inline void chkmax(ll &x, ll y) {x = max(x, y);}
inline ll S(ll x) {return ds == 1 ? x : x * x;}
inline ll V(ll x) {return dv == 1 ? x : x * x;}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m >> ds >> dv;
    for (int i = 1; i <= n; i++) cin >> a[i].s >> a[i].v;
    sort(a + 1, a + n + 1, cmps);
    ll ans = 0;
    for (int len = 1; len < m; len++) {
        int sv = 0;
        for (int i = 1; i <= len; i++) sv += a[i].v;
        for (int i = len + 1; chkmax(ans, V(sv) - S(a[i - 1].s - a[i - len].s)), i <= n; sv += a[i].v - a[i - len].v, i++);
    }
    for (int i = 1; i <= n; i++) a[i].id = i, b[i] = a[i], pre[i] = i - 1, lat[i] = i + 1;
    sort(b + 1, b + n + 1, cmpv);
    for (int j = 1; j <= n - m + 1; j++) {
        int p = b[j].id, tot = 0;
        for (int i = 2, cur = pre[p]; i <= m && cur; i++, cur = pre[cur]) c[++tot] = a[cur];
        reverse(c + 1, c + tot + 1);
        for (int i = 1, cur = p; i <= m && cur <= n; i++, cur = lat[cur]) c[++tot] = a[cur];
        int sv = 0;
        for (int i = 1; i <= m; i++) sv += c[i].v;
        for (int i = m + 1; chkmax(ans, V(sv) - S(c[i - 1].s - c[i - m].s)), i <= tot; sv += c[i].v - c[i - m].v, i++);
        pre[lat[p]] = pre[p], lat[pre[p]] = lat[p];
    }
    cout << ans;
    return 0;
}

B. To Do Tree

考虑没有 \(m\) 的限制时怎么做,显然直接拓扑排序即可。

现在有了 \(m\) 的限制,贪心地选子树大的,然后全对了。

证明不会。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

constexpr int N = 1e5 + 10;

int n, m, fa[N], sz[N];

vector<int> G[N];

struct cmp {const bool operator()(const int &lhs, const int &rhs) {return sz[lhs] < sz[rhs];}};
priority_queue<int, vector<int>, cmp> q;

void dfs(int u) {
    sz[u] = 1;
    for (int v : G[u]) dfs(v), sz[u] += sz[v];
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m;
    for (int i = 2; i <= n; i++) cin >> fa[i], G[fa[i]].emplace_back(i);
    dfs(1);

    vector<vector<int>> ans;
    vector<int> op;

    q.emplace(1);
    while (!q.empty()) {
        op.clear();
        for (int i = 1; i <= m && !q.empty(); i++) {op.emplace_back(q.top()); q.pop();}
        ans.emplace_back(op);
        for (int u : op) for (int v : G[u]) q.emplace(v);
    }

    cout << ans.size();
    for (auto i : ans) {
        cout << '\n' << i.size();
        for (int j : i) cout << ' ' << j;
    }
    return 0;
}

C. 配对树

考虑这样一种情况,\(x, y\) 都在以 \(u\) 为根的子树上,但他们没有配对,实际上的配对情况是 \((x, x'), (y, y')\),此时把配对改为 \((x, y), (x', y')\) 一定不劣。

所以一定存在一个最优匹配,使得所有路径不交。

考虑每条边对答案的贡献。

\((fa_u, u)\) 会被记入答案当且仅当以 \(u\) 为根的子树内 ddl 数量为奇数。

我们把 \(u\) 子树内的 ddl 视作 \(1\),其余的视作 \(0\),对于一个固定的 \(u\),我们需要知道这个 \(01\) 序列里 \(1\) 的个数是奇数的子区间数。

\(01\) 序列做前缀和得到 \(s\),所求即 \(s_r - s_{l - 1} \equiv 1 \pmod2, l - 1 \equiv r \pmod2\)\((l, r)\) 的个数,分下标和值的奇偶四种情况讨论就好。

时间复杂度 \(\mathcal O(n^2 + nm)\)

用线段树合并维护 \(s\),时间复杂度 \(\mathcal O(n + m \log m)\)

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

constexpr int N = 1e5 + 10, TN = N * 40, MOD = 998244353;

int n, m, rt[N], ans;

int tot, head[N];
struct Edge {int to, nxt, val;} e[N << 1];
inline void add(int u, int v, int w) {e[++tot] = Edge{v, head[u], w}, head[u] = tot;}

namespace SGT {
    #define lson t[pos].ls
    #define rson t[pos].rs

    int tot;
    struct Seg {int c[2][2], ls, rs; bool s;} t[TN];

    inline void pu(int pos, int l, int r) {
        int mid = (l + r) >> 1;
        for (int i : {0, 1}) for (int j : {0, 1}) {
            t[pos].c[i][j] = 0;
            if (lson) t[pos].c[i][j] += t[lson].c[i][j];
            else if (!j) t[pos].c[i][j] += ((mid + 2 + i) >> 1) - ((l + 1 + i) >> 1); 
            if (rson) t[pos].c[i][j] += t[rson].c[i][j ^ t[lson].s];
            else if (j == t[lson].s) t[pos].c[i][j] += ((r + i) >> 1) - ((mid + i) >> 1);
        }
        t[pos].s = t[lson].s ^ t[rson].s;
    }

    void upd(int &pos, int l, int r, int x) {
        if (!pos) pos = ++tot;
        if (l == r) {t[pos].s = 1, t[pos].c[l & 1][1] = 1; return;}
        int mid = (l + r) >> 1;
        x <= mid ? upd(lson, l, mid, x) : upd(rson, mid + 1, r, x);
        pu(pos, l, r);
    }

    int merge(int x, int y, int l, int r) {
        if (!x || !y) return x ^ y;
        int mid = (l + r) >> 1;
        t[x].ls = merge(t[x].ls, t[y].ls, l, mid), t[x].rs = merge(t[x].rs, t[y].rs, mid + 1, r);
        pu(x, l, r); return x;
    }

    inline ll query(int pos) {return (1ll * t[pos].c[0][1] * t[pos].c[0][0] + 1ll * t[pos].c[1][0] * t[pos].c[1][1]) % MOD;}
}

void dfs(int u, int fa) {
    for (int i = head[u], v; v = e[i].to, i; i = e[i].nxt) if (v != fa) {
        dfs(v, u);
        ans = (ans + SGT::query(rt[v]) * e[i].val) % MOD;
        rt[u] = SGT::merge(rt[u], rt[v], 0, m);
    }
}

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n >> m;
    for (int i = 1, u, v, w; i < n; i++) cin >> u >> v >> w, add(u, v, w), add(v, u, w);
    for (int i = 1, x; i <= m; i++) cin >> x, SGT::upd(rt[x], 0, m, i);
    dfs(1, -1);
    cout << ans;
    return 0;
}
posted @ 2024-05-29 13:33  Chy12321  阅读(6)  评论(0编辑  收藏  举报