ICode9

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

CF #526 部分题解

2022-08-21 00:34:24  阅读:124  来源: 互联网

标签:ch int 题解 MN CF 526 inline Mod define


传送门

CF1083C Max Mex

求一条 \(\text{mex}\) 值最大的路径,相当于求一个最大的前缀 \(0,1,2,\cdots,k\) 使得点权为 \(0,1,\cdots,k\) 的点都可以被包含在同一条链中。

考虑使用线段树维护,第 \(i\) 个位置存树上点权为 \(i\) 的点的编号,如果我们能对每个区间求出其是否构成链以及对应链的信息,那么查询就只需要线段树上二分就行了。考虑如何合并信息,对于两个儿子区间,若任意一个区间内的点不构成链,那合起来肯定也不构成链。若两边都构成链,那么新的链的端点一定是这其中的两个,因此枚举 \(\binom{4}{2} = 6\) 种可能,判断一下剩下两个点是否在这条链上即可。

现在只需要判断一个点是否在一条链上。这是容易的,\(x\) 在链 \(u\to v\) 上的充要条件是 \(\text{dist}(x,u)+\text{dist}(x,v)=\text{dist}(u,v)\),因此只需要快速求树上两点间距离,使用 \(\text{ST}\) 表预处理一下 \(\text{LCA}\) 即可。时间复杂度 \(O((n+q)\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) 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;
}

const int MN = 4e5 + 5;
const int Mod = 998244353;
const int inf = 1e9;

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

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, Q, a[MN], p[MN], dis[MN], dfn[MN], dfc, lg[MN];
pii ST[MN][20];
vector <int> e[MN];

inline void DFS(int u, int pr, int d) {
    dfn[u] = ++dfc, ST[dfc][0] = mp(d, u);
    for (int v : e[u]) if (v != pr) {
        dis[v] = dis[u] + 1;
        DFS(v, u, d + 1);
        dfn[u] = ++dfc, ST[dfc][0] = mp(d, u);
    }
}
inline void buildST() {
    for (int i = 2; i <= dfc; i++) lg[i] = lg[i >> 1] + 1;
    for (int j = 1; j <= 19; j++) 
        for (int i = 1; i + (1 << j) - 1 <= dfc; i++)
            ST[i][j] = min(ST[i][j - 1], ST[i + (1 << j - 1)][j - 1]);
}
inline int qry(int u, int v) {
    int l = dfn[u], r = dfn[v];
    if (l > r) swap(l, r);
    int k = lg[r - l + 1];
    int q = min(ST[l][k], ST[r - (1 << k) + 1][k]).se;
    return dis[u] + dis[v] - 2 * dis[q];
}
inline bool chk(int p, int x, int y) {
    return qry(p, x) + qry(p, y) == qry(x, y);
}

struct Dat {
    int x, y, ty;
    Dat(int _x = 0, int _y = 0, int _ty = 0) : x(_x), y(_y), ty(_ty) {}
};
inline Dat operator + (const Dat x, const Dat y) {
    if (x.ty == 2) return y;
    if (y.ty == 2) return x;
    if ((!x.ty) || (!y.ty)) return Dat(0, 0, 0);
    int p = x.x, q = x.y, r = y.x, s = y.y;
    if (chk(p, r, s) && chk(q, r, s)) return Dat(r, s, 1);
    if (chk(p, q, s) && chk(r, q, s)) return Dat(q, s, 1);
    if (chk(p, q, r) && chk(s, q, r)) return Dat(q, r, 1);
    if (chk(r, p, q) && chk(s, p, q)) return Dat(p, q, 1);
    if (chk(q, p, r) && chk(s, p, r)) return Dat(p, r, 1);
    if (chk(q, p, s) && chk(r, p, s)) return Dat(p, s, 1);
    return Dat(0, 0, 0);
}

const int MS = MN << 2;
#define ls o << 1
#define rs o << 1 | 1
#define mid ((l + r) >> 1)
#define LS ls, l, mid
#define RS rs, mid + 1, r
Dat tr[MS];
inline void pushup(int o) {
    tr[o] = tr[ls] + tr[rs];
}
inline void build(int o, int l, int r) {
    if (l == r) return tr[o] = Dat(p[l], p[l], 1), void();
    build(LS), build(RS), pushup(o);
}
inline void upd(int o, int l, int r, int p, Dat v) {
    if (l == r) return tr[o] = v, void();
    (p <= mid ? upd(LS, p, v) : upd(RS, p, v)), pushup(o);
}   
inline int qry(int o, int l, int r, Dat v) {
    if ((tr[o] + v).ty == 1) return r;
    if (l == r) return l - 1;
    if ((tr[ls] + v).ty == 0) return qry(LS, v);
    return qry(RS, v + tr[ls]);
}

signed main(void) {
    N = read();
    for (int i = 1; i <= N; i++) a[i] = read(), p[++a[i]] = i;
    for (int i = 2; i <= N; i++) {
        int f = read();
        e[f].pb(i), e[i].pb(f);
    }
    DFS(1, 0, 1), buildST(), build(1, 1, N);
    Q = read();
    while (Q--) {
        int op = read();
        if (op == 1) {
            int x = read(), y = read();
            upd(1, 1, N, a[x], Dat(y, y, 1));
            upd(1, 1, N, a[y], Dat(x, x, 1));
            swap(a[x], a[y]);
        } else {
            printf("%lld\n", qry(1, 1, N, Dat(0, 0, 2)));
        }
    }
    return 0;
}

CF1083D The Fair Nut's getting crazy

显然考虑枚举每个交集对答案的贡献。定义 \(\text{pre}_i\) 为 \(a_i\) 上一次出现的位置(若不存在则值为 \(0\)),\(\text{suf}_i\) 为 \(a_i\) 下一次出现的位置(若不存在则值为 \(n+1\))。记:

\[f(i,j) = \max\{ \text{pre}_i,\cdots,\text{pre}_j \} + 1, \ g(i,j) = \min\{ \text{suf}_i,\cdots,\text{suf}_j \} - 1 \]

那么答案即为

\[\sum_{i=1}^n \sum_{j=i}^n [i \geq f(i,j)][j \leq g(i,j)](i - f(i,j))(g(i,j) - j) \]

考虑从大到小枚举 \(i\),合法的 \(j\) 一定是一个区间。显然 \(f(i,j)\) 单调不降,\(g(i,j)\) 单调不升,可以双指针算出最大的 \(j\) 的位置。现在的问题是,对于当前的 \(i,j\),如何快速计算

\[\sum_{k = i}^j (i - f(i,k)) \times (g(i,k) - k) \]

把括号拆开

\[i \times \sum_{k = j}^i g(i,k) - i \times \sum_{k=i}^j k - \sum_{k=j}^i f(i,k) \times g(i,k) + \sum_{k = j}^i f(i,k) \times k \]

使用线段树维护,用单调栈预处理后取 \(\max\) 和 \(\min\) 的操作就变成了区间赋值。要维护以下几种操作:\(a\) / \(b\) 序列区间赋值,求 \(a\) / \(b\) 序列区间和,求 \(a\) / \(b\) 序列 \((a/b)_i \times i\) 和,求区间 \(a_i \times b_i\) 和。打打标记容易维护,时间复杂度 \(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) 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;
}

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

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

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, a[MN], b[MN], lst[MN], cnt[MN], pre[MN], suf[MN], C[MN], D[MN], stk[MN], tp;

inline void inc(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; }
inline int add(int x, int y) { return x + y > Mod ? x + y - Mod : x + y; }
inline int sub(int x, int y) { return x - y < 0 ? x - y + Mod : x - y; }
inline int s2(int x) { return x * (x + 1) / 2 % Mod; }
inline int S(int l, int r) { return sub(s2(r), s2(l - 1)); }

const int MS = MN << 2;
#define ls o << 1
#define rs o << 1 | 1
#define mid ((l + r) >> 1)
#define LS ls, l, mid
#define RS rs, mid + 1, r
int tr[MS][4], tag[MS][2];
inline void pushup(int o) {
    for (int i = 0; i < 4; i++) tr[o][i] = add(tr[ls][i], tr[rs][i]);
}
inline void pushtag(int o, int l, int r, int v, int ty) {
    tr[o][2] = tr[o][ty ^ 1] * v % Mod;
    tr[o][ty] = (r - l + 1) * v % Mod;
    tag[o][ty] = v;
    if (!ty) tr[o][3] = v * S(l, r) % Mod;
}
inline void pushdown(int o, int l, int r) {
    for (int t = 0; t < 2; t++) if (tag[o][t]) {
        pushtag(LS, tag[o][t], t), pushtag(RS, tag[o][t], t);
        tag[o][t] = 0;
    }
}
inline void upd(int o, int l, int r, int L, int R, int v, int ty) {
    if (r < L || l > R) return;
    if (L <= l && R >= r) return pushtag(o, l, r, v, ty);
    pushdown(o, l, r), upd(LS, L, R, v, ty), upd(RS, L, R, v, ty), pushup(o);
}
inline int qry(int o, int l, int r, int L, int R, int ty) {
    if (r < L || l > R || L > R) return 0;
    if (L <= l && R >= r) return tr[o][ty];
    return pushdown(o, l, r), add(qry(LS, L, R, ty), qry(RS, L, R, ty));
}

signed main(void) {
    N = read();
    for (int i = 1; i <= N; i++) a[i] = read(), b[i] = a[i];
    sort(b + 1, b + N + 1);
    int bcnt = unique(b + 1, b + N + 1) - b - 1;
    for (int i = 1; i <= N; i++) a[i] = lob(b + 1, b + bcnt + 1, a[i]) - b;
    for (int i = 1; i <= N; i++) pre[i] = lst[a[i]] + 1, lst[a[i]] = i;
    for (int i = 1; i <= bcnt; i++) lst[i] = N + 1;
    for (int i = N; i >= 1; i--) suf[i] = lst[a[i]] - 1, lst[a[i]] = i;
    for (int i = N; i >= 1; i--) {
        while (tp && pre[i] > pre[stk[tp]]) tp--;
        C[i] = tp ? stk[tp] - 1 : N, stk[++tp] = i;
    }
    tp = 0;
    for (int i = N; i >= 1; i--) {
        while (tp && suf[i] < suf[stk[tp]]) tp--;
        D[i] = tp ? stk[tp] - 1 : N, stk[++tp] = i;
    }
    int ans = 0;
    for (int i = N, j = N; i >= 1; i--) {
        upd(1, 1, N, i, C[i], pre[i], 0);
        upd(1, 1, N, i, D[i], suf[i], 1);
        cnt[a[i]]++;
        while (cnt[a[i]] > 1) cnt[a[j]]--, j--;
        inc(ans, qry(1, 1, N, i, j, 3));
        inc(ans, i * qry(1, 1, N, i, j, 1) % Mod);
        dec(ans, i * S(i, j) % Mod);
        dec(ans, qry(1, 1, N, i, j, 2));
    }
    printf("%lld\n", ans);
    return 0;
}

CF1083E The Fair Nut and Rectangles

因为矩形互不包含,先对所有矩形排个序,使得 \(x_i\) 单调递增,\(y_i\) 单调递减。考虑 DP,设 \(f_i\) 为选到排序后的第 \(i\) 个矩形,钦定第 \(i\) 个矩形必选的情况下面积并减去权值和的最大值为 \(f_i\)。转移枚举上一个选择的矩形是谁,则

\[f_i = \max_{0 \leq j \leq i-1} \{f_j + S_i - a_i - S_j \cap S_i \} \]

其中 \(S_i = x_iy_i\),\(S_i \cap S_j = x_jy_i\)。由单调性容易说明其正确性。然后一眼斜率优化,假设 \(i\) 的转移点为 \(j\),那么

\[f_i = f_j + x_iy_j - x_jy_i - a_i \]

移项可得

\[\underline{f_j}_y = \underline{y_i}_k \underline{x_j}_x + \underline{f_i - x_iy_i +a_i}_b \]

相当于平面上若干个点 \((x_j,f_j)\),过每个点做斜率为 \(y_i\) 的直线,我们的目标是最大化截距 \(b\)。维护上凸壳,由于 \(x\) 递增,\(y\)(斜率)递减,单调队列维护即可。时间复杂度 \(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) 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;
}

const int MN = 2e6 + 5;
const int Mod = 1e9 + 7;
const int inf = 1e9;
const double eps = 1e-10;

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

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, l, r, q[MN], f[MN], ans;

struct Square {
    int x, y, w;
    inline bool operator < (const Square &p) const {
        return y > p.y;
    }
} s[MN];

#define X(i) s[i].x
#define Y(i) f[i] 
inline double slope(int i, int j) { 
    return abs(X(j) - X(i)) < eps ? inf : (Y(j) - Y(i)) / (X(j) - X(i)); 
}
 
signed main(void) {
    N = read();
    for (int i = 1; i <= N; i++) s[i].x = read(), s[i].y = read(), s[i].w = read();
    sort(s + 1, s + N + 1);
    for (int i = 1; i <= N; i++) {
        while (l < r && slope(q[l], q[l + 1]) >= s[i].y) l++;
        f[i] = s[i].x * s[i].y - s[i].w;
        if (l <= r) f[i] = max(f[i], f[q[l]] + (s[i].x - s[q[l]].x) * s[i].y - s[i].w);
        while (l < r && slope(q[r - 1], q[r]) <= slope(q[r], i)) r--;
        q[++r] = i;
    }
    for (int i = 1; i <= N; i++) ans = max(ans, f[i]);
    printf("%lld\n", ans);
    return 0;
}

CF1083F The Fair Nut and Amusing Xor

首先差分,这样操作就变成了将距离为 \(k\) 的两个数 \(\oplus\) 一个值,单点修改变成了修改两个值(还是单点修改)。

考虑如何计算答案,我们可以从左往右扫,如果某个 \(a_i\) 不为 \(0\) 就将它和 \(a_{i+k}\) 一起异或 \(a_i\) 就行了。显然 \(\text{mod} \ k\) 意义下每条链都是独立的,考虑对每条链分别计算,不难发现一个数需要被操作当且仅当它的前缀异或和不为 \(0\),我们容斥成计算前缀异或和为 \(0\) 的位置个数。无解的条件就是某条链的异或和不为 \(0\),这个维护一下每条链最后一个位置的前缀异或值有多少个 \(0\) 就行了。

现在我们要维护两种操作:单点修改,维护前缀异或和。后者相当于是对后缀的区间修改,直接分块,散块暴力重构,整块打个 \(\mathrm{tag}\) 就行了。具体实现的时候可以把所有序列接在一起维护,只不过后缀修改就变成了区间修改,做法是一样的。时间复杂度 \(O(m \sqrt 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) 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;
}

const int MN = 2e5 + 5;
const int MM = 1.7e4 + 5;
const int MB = 350;
const int MS = (MN + MB - 1) / MB + 5;
const int Mod = 1e9 + 7;
const int inf = 1e9;

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

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, Q, a[MN], id[MN], seq[MN], tp, bel[MN], zero;
int bl[MS], br[MS], cnt[MS][MM], tag[MS], A[MN], B[MN], lst[MN], C[MN], D[MN];
char op[10];

inline void pushdown(int x) {
    for (int i = bl[x]; i <= br[x]; i++) cnt[x][D[i]]--, D[i] ^= tag[x];
    tag[x] = 0;
}
inline void rebuild(int x) {
    for (int i = bl[x]; i <= br[x]; i++) cnt[x][D[i]]++;
}
inline void upd(int x, int y) {
    if (C[x % M]) zero--;
    C[x % M] ^= y;
    if (C[x % M]) zero++;
    int l = id[x], r = lst[x % M];
    pushdown(bel[l]);
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) D[i] ^= y;
        return rebuild(bel[l]);
    }
    pushdown(bel[r]);
    for (int i = l; i <= br[bel[l]]; i++) D[i] ^= y;
    for (int i = bl[bel[r]]; i <= r; i++) D[i] ^= y;
    rebuild(bel[l]), rebuild(bel[r]);
    for (int i = bel[l] + 1; i < bel[r]; i++) tag[i] ^= y;
}
inline void getans() {
    int ans = 0;
    for (int j = 1; j <= bel[N + 1]; j++) ans += cnt[j][tag[j]];
    printf("%lld\n", N + 1 - ans);
}

signed main(void) {
    N = read(), M = read(), Q = read();
    for (int i = 1; i <= N; i++) A[i] = read(), a[i] ^= A[i];
    for (int i = 1; i <= N; i++) B[i] = read(), a[i] ^= B[i];
    for (int i = 1; i <= N + 1; i++) bel[i] = (i - 1) / MB + 1, br[bel[i]] = i;
    for (int i = N + 1; i >= 1; i--) bl[bel[i]] = i;
    for (int i = N + 1; i >= 1; i--) a[i] ^= a[i - 1];
    for (int i = M + 1; i <= N + 1; i++) a[i] ^= a[i - M];
    for (int i = N - M + 2; i <= N + 1; i++) if (C[i % M] = a[i]) zero++;
    for (int i = 1; i <= M; i++) 
        for (int j = i; j <= N + 1; j += M) seq[id[j] = ++tp] = j, lst[i % M] = tp;
    for (int i = 1; i <= bel[N + 1]; i++) 
        for (int j = bl[i]; j <= br[i]; j++) cnt[i][D[j] = a[seq[j]]]++;
    if (zero) puts("-1");
    else getans();
    while (Q--) {
        int x, y;
        scanf("%s%lld%lld", op, &x, &y);
        if (op[0] == 'a') upd(x, A[x] ^ y), upd(x + 1, A[x] ^ y), A[x] = y;
        else upd(x, B[x] ^ y), upd(x + 1, B[x] ^ y), B[x] = y;
        if (zero) puts("-1");
        else getans();
    }
    return 0;
}

标签:ch,int,题解,MN,CF,526,inline,Mod,define
来源: https://www.cnblogs.com/came11ia/p/16609143.html

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

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

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

ICode9版权所有