ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF Round Hello 2022 部分题解

2022-08-02 20:02:53  阅读:160  来源: 互联网

标签:ch int 题解 MN CF vr Mod Round define


来补个档。

CF1621G Weighted Increasing Subsequences

先离散化。对每个上升子序列计算权值是困难的,我们考虑每个位置对答案的贡献。

即我们想要知道对于每个 \(a_p\),\(i_k\) 最远能到哪里,使得存在一个 \(x \in (i_k, n]\) 满足 \(a_x > a_i\)。容易发现,若设 \(r_p\) 为最右端的满足 \(a_{r_p} > a_p\) 的下标,那么 \(i_k < r_p\)。

于是对于每一个 \(p\) 我们需要计算 “经过 \(p\),且最后一个元素在 \(r_p\) 之前的” 上升子序列个数,这可以容斥成 “经过 \(p\),且最后一个元素在 \(r_p\) 或 \(r_p\) 之后” 的上升子序列个数。但由于 \(r_p\) 的定义,对于任意 \(i \in (r_p, n]\) 都有 \(a_i \leq a_p\),这意味着这些位置不可能成为上升子序列的结尾元素,于是我们只需要计算 “经过 \(p\),且最后一个元素位置为 \(r_p\)” 的上升子序列个数。

接下来考虑如何计数。显然考虑把子序列拆成 \(p\) 之前和 \(p\) 到 \(r_p\) 两部分,前面是好算的。\(p\) 之后的部分我们考虑倒着处理,由于 \(r_p\) 是最靠右的满足条件的下标,所以任意一个从 \(p\) 开始的子序列结尾一定不超过 \(r_p\),只需要维护 “从 \(p\) 开始以最远位置结尾” 的子序列的方案数即可。时间复杂度 \(O(n \log n)\)。

Code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v, n) memset(x, v, sizeof(int) * (n))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

const int MN = 2e5 + 5;
const int Mod = 1e9 + 7;

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

// #define dbg

int N, M, a[MN], pre[MN], suf[MN], ans;
vector <int> vr;

inline void add(int &x, int y) {
    x += y; if (x >= Mod) x -= Mod;
}
inline void dec(int &x, int y) {
    x -= y; if (x < 0) x += Mod;
}

int c[MN], mx[MN], mxv[MN];
inline void Add1(int x, int v) { for (; x <= N; x += x & -x) add(c[x], v); }
inline int Qry1(int x) { int res = 0; for(; x; x -= x & -x) add(res, c[x]); return res; }
inline void Add2(int x, pii v) {
    for (; x; x -= x & -x) {
        if (mx[x] < v.fi) mx[x] = v.fi, mxv[x] = 0;
        if (mx[x] == v.fi) add(mxv[x], v.se);
    }
}
inline pii Qry2(int x) {
    int Mx = 0, Mxv = 0;
    for (; x <= N; x += x & -x) {
        if (Mx < mx[x]) Mx = mx[x], Mxv = 0;
        if (Mx == mx[x]) add(Mxv, mxv[x]);
    }
    return mp(Mx, Mxv);
}
inline void Work() {
    N = read();
    vr.clear(), ans = 0;
    for (int i = 1; i <= N; i++) a[i] = read(), vr.pb(a[i]);
    for (int i = 1; i <= N; i++) c[i] = mx[i] = mxv[i] = 0;
    sort(vr.begin(), vr.end());
    vr.erase(unique(vr.begin(), vr.end()), vr.end());
    for (int i = 1; i <= N; i++) a[i] = lob(vr.begin(), vr.end(), a[i]) - vr.begin() + 1;
    for (int i = 1; i <= N; i++) Add1(a[i], pre[i] = Qry1(a[i] - 1) + 1);
    for (int i = 1; i <= N; i++) c[i] = 0;
    for (int i = N; i >= 1; i--) {
        Add1(N - a[i] + 1, suf[i] = Qry1(N - a[i]) + 1);
        pii cur = Qry2(a[i] + 1);
        if (!cur.se) cur = mp(i, 1);
        dec(suf[i], cur.se), Add2(a[i], cur);
    }
    for (int i = 1; i <= N; i++) add(ans, pre[i] * suf[i] % Mod);
    printf("%lld\n", ans);
}

signed main(void) {
    int T = read();
    while (T--) Work();
    return 0;
}

CF1621H Trains and Airplanes

注意到从任意一点 \(x\) 开始,其在每个区域被查的次数是固定的,设为 \(s_{x,i}\),那么 \(f(v)\) 可以写成:

\[f(v) = \sum_{i \neq z_v} \min(fine_{v,i} \times s_{v,i},pass_{v,i}) \]

但是询问很奇怪,直接写成上式并不好处理询问。考虑在同一个询问中的所有点它们的 \(s_{v,i}\),对于这些点来说,往上走时经过的所有区域在时间轴上都形成一段区间,而 \(s_{v,i}\) 计算的就是在这段区间中 \(T\) 的倍数点出现的次数。考虑把这些线段在 \(\mathrm{mod} \ T\) 意义下处理,根据每个点的深度不同,时间轴和线段的位置也不相同。设在询问中 \(s_{v,i}\) 的最小值为 \(mn_{i}\),容易发现 \(s_{v,i}\) 的值只可能为 \(mn_i\) 或 \(mn_i + 1\),并且当线段在 \(\mathrm{mod} \ T\) 意义下左右端点相对位置不变时取 \(mn_i\),否则取 \(mn_i + 1\)。

也就是说,在同一次询问中,不同的 \(s_{v,i}\) 至多只有 \(O(2^k)\) 种,但还是太多了。更进一步地,考虑一个类似扫描线的过程,不难发现线段的移动是连续的,同样在 \(\mathrm{mod} \ T\) 意义考虑,取 \(mn_i\) 的位置恰是一个区间!也就是说,这 \(k\) 个区域把 \(\mathrm{mod} \ T\) 的环分成了 \(O(k)\) 个部分,这样在同一次询问中只会有 \(O(k)\) 种不同的 \(s_{v,i}\)。

接下来只需要模拟这个过程就行了:对每个位置预处理出 \(s_{v,i}\),然后对每个位置处理出一个二进制状态 \(\mathrm{mask}\) 表示每个区域是取 \(mn_i\) 还是取 \(mn_{i+1}\),这样的状态至多只有 \(O(k)\) 个。然后从下往上合并出每次询问需要的状态,处理询问就很容易了。时间复杂度 \(O(nk \log k + qk^2)\),听说能更快,但反正过了就不管了。

Code
/*
最黯淡的一个 梦最为炽热
万千孤单焰火 让这虚构灵魂鲜活
至少在这一刻 热爱不问为何
存在为将心声响彻
*/
#include <bits/stdc++.h>
#define pii pair<int, int>
#define mp(x, y) make_pair(x, y)
#define pb push_back
#define eb emplace_back
#define fi first
#define se second
#define int long long
#define mem(x, v) memset(x, v, sizeof(x))
#define mcpy(x, y, n) memcpy(x, y, sizeof(int) * (n))
#define lob lower_bound
#define upb upper_bound
using namespace std;

inline int read() {
	int x = 0, w = 1;char ch = getchar();
	while (ch > '9' || ch < '0') { if (ch == '-')w = -1;ch = getchar(); }
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
	return x * w;
}

inline int min(int x, int y) { return x < y ? x : y; }
inline int max(int x, int y) { return x > y ? x : y; }

const int MN = 2e5 + 5;
const int MK = 30;
const int Inf = 2e18;
const int Mod = 1e9 + 7;

inline int qPow(int a, int b = Mod - 2, int ret = 1) {
    while (b) {
        if (b & 1) ret = ret * a % Mod;
        a = a * a % Mod, b >>= 1;
    }
    return ret;
}

// #define dbg

int N, T, K, Q, zone[MN], pass[MN], fine[MN]; char s[MN];
int dis[MN], fa[MN], ord[MN], dfc, up[MN], lim[MN], coef[MN][MK], mask[MN];
vector <pii> G[MN];
vector <int> vr[MN];

inline void DFS(int u) {
    ord[++dfc] = u;
    for (auto [v, w] : G[u]) {
        if (v == fa[u]) continue;
        fa[v] = u;
        dis[v] = dis[u] + w;
        DFS(v);
    }
}
inline void Work() {
    N = read();
    for (int i = 1; i < N; i++) {
        int u = read(), v = read(), w = read();
        G[u].pb(mp(v, w));
        G[v].pb(mp(u, w));
    }
    fa[1] = -1;
    DFS(1);
    K = read();
    scanf("%s", s + 1);
    for (int i = 1; i <= N; i++) zone[i] = s[i] - 'A' + 1;
    for (int i = 1; i <= K; i++) pass[i] = read();
    for (int i = 1; i <= K; i++) fine[i] = read();
    T = read();
    mem(up, -1);
    for (int j = 1; j <= dfc; j++) {
        int i = ord[j];
        if (fa[i] != -1) {
            if (zone[i] != zone[fa[i]]) {
                up[i] = fa[i];
            } else {
                up[i] = up[fa[i]];
            }
        } else {
            up[i] = -1;
        }
        int p = up[i];
        while (p != -1) {
            int nxt = up[p];
            int L = dis[i] - dis[p];
            int R = (nxt == -1 ? dis[i] : dis[i] - dis[nxt]);
            int c = (R + T - 1) / T - (L + T - 1) / T;
            int mx = (R - L + T - 1) / T;
            coef[i][zone[p]] += mx;
            if (c < mx) {
                mask[i] |= (1 << zone[p]);
            }
            p = nxt;
        }
    }
    for (int j = dfc; j >= 1; j--) {
        int i = ord[j];
        vr[i].pb(mask[i]);
        sort(vr[i].begin(), vr[i].end());
        vr[i].resize(unique(vr[i].begin(), vr[i].end()) - vr[i].begin());
        if (fa[i] != -1 && zone[i] == zone[fa[i]]) {
            vr[fa[i]].insert(vr[fa[i]].end(), vr[i].begin(), vr[i].end());
        }
    }
    for (int i = 1; i <= K; i++) lim[i] = pass[i] / fine[i];
    Q = read();
    while (Q--) {
        int op = read();
        if (op == 1 || op == 2) {
            string ch;
            cin >> ch;
            int id = ch[0] - 'A' + 1;
            (op == 1 ? pass[id] : fine[id]) = read();
            lim[id] = pass[id] / fine[id];
        } else {
            int u = read();
            int ans = Inf;
            for (int ms : vr[u]) {
                int cur = 0;
                for (int j = 1; j <= K; j++) {
                    int c = coef[u][j];
                    if (ms & (1 << j)) c--;
                    if (c <= lim[j]) {
                        cur += c * fine[j];
                    } else {
                        cur += pass[j];
                    }
                }
                ans = min(ans, cur);
            }
            printf("%lld\n", ans);
        }
    }
}

signed main(void) {
    int T = 1;
    while (T--) Work();
    return 0;
}

标签:ch,int,题解,MN,CF,vr,Mod,Round,define
来源: https://www.cnblogs.com/came11ia/p/16544936.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有