Featured image of post Round 1124

Round 1124

A/B

弱智题目。半小时写不完的应该加训简单Div.2 adhoc。

C

题目大意:给定一个数组 $a_1,a_2,\ldots,a_n$ ,请求出对于任意 $l \lt r$ , $(a_l \& m) \oplus (a_{l+1} \& m) \ldots \oplus (a_r \& m)$ 的最大值,其中 $m=\max(a_l,a_{l+1},\ldots,a_r)$ 。

数据范围: $2 \leq \sum n \leq 2 \cdot 10^5,0 \leq a_i < 2^{18}$

思路:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 笛卡尔树,默认为大根堆
template <class T, typename Compare = std::greater<T>>
struct CartesianTree {
    T inf = std::numeric_limits<T>::max();

    struct Node {
        int idx;               // 原数组下标
        T val;                 // 权值
        int par, siz;          // 父节点索引,子树大小
        std::array<T, 2> son;  // 左儿子,右儿子

        Node(int idx = 0, T val = 0, int par = 0, int siz = 0) : idx(idx), val(val), par(par), siz(siz), son{} {}
    };

    std::vector<Node> t;

    CartesianTree() { init(); }

    void init(Compare comp = Compare()) {
        t.assign(1, {0, 0, 0});
        t[0].son.fill(0);
        if (comp(-inf, inf))
            t[0].val = -inf;
        else
            t[0].val = inf;
    }  // 自动建立虚拟节点

    void add(int idx, T val, int par = 0) { t.emplace_back(idx, val, par); }  // 负责把元素加进末尾,需要使用1-based

    int work(Compare comp = Compare()) {
        for (int i = 1; i < t.size(); i++) {
            int k = i - 1;
            while (comp(t[i].val, t[k].val)) k = t[k].par;
            t[i].son[0] = t[k].son[1];
            t[k].son[1] = i;
            t[i].par = k;
            t[t[i].son[0]].par = i;
        }  // 遍历,砍树枝
        auto dfs = [&](auto&& dfs, int u) -> void {
            if (!u) return;
            t[u].siz = 1;
            dfs(dfs, ls(u));
            dfs(dfs, rs(u));
            t[u].siz += t[ls(u)].siz + t[rs(u)].siz;
        };  // 进行一个dfs
        dfs(dfs, t[0].son[1]);
        return t[0].son[1];
    }

    int Left(int p) { return p - size(ls(p)); }  // 左边最远

    int Right(int p) { return p + size(rs(p)); }  // 右边最远

    int size(int p) { return t[p].siz; }

    int ls(int p) { return t[p].son[0]; }

    int rs(int p) { return t[p].son[1]; }

    int par(int p) { return t[p].par; }
};
/* 使用示例
CartesianTree<int, less<int>> ct;

rep(i, 0, n - 1) { ct.add(i + 1, nums[i]); }

int root = ct.work(less<int>());
rep(i, 1, n) {
    int L = ct.Left(i);
    int R = ct.Right(i);
}
*/
const ll MX = 1LL << 18;
ll las[MX];
ll tag[MX];
ll tim = 0;
void solve() {
    ll n;
    cin >> n;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vl pre(n + 1);
    rep(i, 1, n) pre[i] = pre[i - 1] ^ a[i - 1];
    CartesianTree<ll> ct;
    rep(i, 0, n - 1) ct.add(i + 1, a[i]);
    ct.work();
    vl l(n + 1), r(n + 1);
    vvl queries(n + 1);
    vector<bool> vis(n + 1, false);
    rep(i, 1, n) {
        l[i] = ct.Left(i), r[i] = ct.Right(i);
        if (i - l[i] + 1 <= r[i] - i + 1)
            queries[r[i]].emplace_back(i);
        else
            vis[i] = true;
    }
    auto check = [&](ll x) -> bool {
        tim++;
        vl pre2(n + 1);
        rep(i, 0, n) pre2[i] = (pre[i] & x);
        rep(i, 0, n) {
            tag[pre2[i]] = tim;
            las[pre2[i]] = i;
            for (auto& j : queries[i]) {
                if ((a[j - 1] & x) != x) continue;
                rep(v, l[j] - 1, j - 1) {
                    ll l1 = max(j, v + 2);
                    if (l1 > i) continue;
                    if (tag[pre2[v] ^ x] == tim && las[pre2[v] ^ x] >= l1) return true;
                }
            }
            if (i + 2 <= n && vis[i + 2] && ((a[i + 1] & x) == x)) {
                if (l[i + 2] - 1 <= i && tag[pre2[i + 2] ^ x] == tim && las[pre2[i + 2] ^ x] >= l[i + 2] - 1) return true;
            }
            if (i + 1 <= n && vis[i + 1] && ((a[i] & x) == x)) {
                rep(v, i + 2, r[i + 1]) {
                    if (l[i + 1] - 1 <= i && tag[pre2[v] ^ x] == tim && las[pre2[v] ^ x] >= l[i + 1] - 1) return true;
                }
            }
        }
        return false;
    };
    ll ans = 0;
    frep(i, 17, 0) {
        if (check(ans | (1LL << i))) ans = (ans | (1LL << i));
    }
    cout << ans << endl;
    return;
}

D

题目大意:给定一个排列 $p$ ,对于每个下标 $i$ ,你可以跳到 $j$ ,当且仅当 $j \lt i$ 或者 $p_j$ 是第一个大于 $p_i$ 的位置,定义 $f(i,j)$ 为从 $i$ 到 $j$ 的最小跳跃数,求 $\sum\limits_{i \not ={j}} f(i,j)$ 。

数据范围: $1 \leq \sum n \leq 10^6$

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct Node {
    int a[3];
    ll b;
};
void solve() {
    ll n;
    cin >> n;
    vi a(n);
    rep(i, 0, n - 1) cin >> a[i], a[i]--;
    ll ans = (n - 1) * n / 2;
    stack<int> s;
    vi r(n, n);
    frep(i, n - 1, 0) {
        while (!s.empty() && a[s.top()] <= a[i]) s.pop();
        if (!s.empty()) r[i] = s.top();
        s.push(i);
    }
    vvi ma(n);
    rep(i, 0, n - 1) {
        if (r[i] != n) ma[r[i]].push_back(i);
    }
    vi siz(n);
    auto dfs = [&](this auto&& dfs, int x, int pa) -> void {
        siz[x] = 1;
        for (auto& p : ma[x]) {
            if (p == pa) continue;
            dfs(p, x);
            siz[x] += siz[p];
        }
        return;
    };
    rep(i, 0, n - 1) {
        if (r[i] == n) dfs(i, -1);
    }
    auto dfs2 = [&](this auto&& dfs2, int x, bool flag) -> Node {
        Node res = {{1, 0, 0}, 0};
        if (ma[x].empty()) return res;
        ll idx = ma[x][0];
        ll re = siz[x] - siz[idx] - 1;
        auto tem = dfs2(idx, flag);
        Node cur = {0, tem.a[0], tem.a[1], tem.b + siz[idx] - tem.a[0] - tem.a[1] - tem.a[2] + 3 * tem.a[2]};
        ans += (flag ? cur.a[1] + 2 * cur.a[2] + cur.b : cur.a[1] + 2 * cur.a[2] + 3 * (siz[idx] - cur.a[0] - cur.a[1] - cur.a[2]));
        Node cur2 = {0, cur.a[0], cur.a[1], cur.b + siz[idx] - cur.a[0] - cur.a[1] - cur.a[2] + 3 * cur.a[2]};
        ans += (flag ? cur2.a[1] + 2 * cur2.a[2] + cur2.b : cur2.a[1] + 2 * cur2.a[2] + 3 * (siz[idx] - cur2.a[0] - cur2.a[1] - cur2.a[2])) * re;
        rep(i, 0, 2) res.a[i] += cur.a[i];
        res.b += cur.b;
        for (auto& p : ma[x]) {
            if (p == idx) continue;
            dfs2(p, false);
            re -= siz[p];
            ans += 1 + 2 * (siz[p] - 1);
            ans += (2 + 3 * (siz[p] - 1)) * re;
            res.a[1]++, res.a[2] += siz[p] - 1;
        }
        return res;
    };
    rep(i, 0, n - 1) {
        if (r[i] == n) dfs2(i, true);
    }
    cout << ans << endl;
    return;
}

E

题目大意:给定数组 $a_1,\ldots,a_n$ ,考虑所有中序遍历结果为它的树,定义一条边的贡献为,删除它后两个连通块各自的异或和为 $X$ 和 $Y$ ,则这条边的贡献为 $X+Y$ ,一棵树的贡献定义为其所有边的贡献之和,请求出所有可能树的总贡献,并模 $998244353$ 。

数据范围: $1 \leq \sum n \leq 2 \cdot 10^5, 0 \leq a_i \lt 2^{18}$

思路:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// NTT求卷积,模数998244353,原根3
// convolution(a, b)返回c,其中c[k]=sum(a[i]*b[j]),i+j=k
constexpr ll MOD = 998244353;
constexpr ll G = 3;

ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) res = res * x % MOD;
        x = x * x % MOD;
        y >>= 1;
    }
    return res;
}

void ntt(vl& a, bool inv) {
    int n = sz(a);
    for (int i = 1, j = 0; i < n; i++) {
        int bit = n >> 1;
        for (; j & bit; bit >>= 1) j ^= bit;
        j ^= bit;
        if (i < j) swap(a[i], a[j]);
    }
    for (int len = 2; len <= n; len <<= 1) {
        ll wlen = qpow(G, (MOD - 1) / len);
        if (inv) wlen = qpow(wlen, MOD - 2);
        for (int i = 0; i < n; i += len) {
            ll w = 1;
            rep(j, 0, len / 2 - 1) {
                ll u = a[i + j];
                ll v = a[i + j + len / 2] * w % MOD;
                a[i + j] = u + v < MOD ? u + v : u + v - MOD;
                a[i + j + len / 2] = u - v >= 0 ? u - v : u - v + MOD;
                w = w * wlen % MOD;
            }
        }
    }
    if (inv) {
        ll inv_n = qpow(n, MOD - 2);
        for (ll& x : a) x = x * inv_n % MOD;
    }
}

vl convolution(vl a, vl b) {
    if (a.empty() || b.empty()) return {};
    int need = sz(a) + sz(b) - 1;
    int n = 1;
    while (n < need) n <<= 1;
    a.resize(n);
    b.resize(n);
    ntt(a, false);
    ntt(b, false);
    for (int i = 0; i < n; i++) a[i] = a[i] * b[i] % MOD;
    ntt(a, true);
    a.resize(need);
    return a;
}

vl polymul(vl& a, vl& b, int lim) {
    vl c = convolution(a, b);
    if (sz(c) > lim) c.resize(lim);
    return c;
}

vl polypow(vl a, int y, int lim) {
    vl res(1, 1);
    while (y > 0) {
        if (y & 1) res = polymul(res, a, lim);
        y >>= 1;
        if (y) a = polymul(a, a, lim);
    }
    return res;
}

/*
使用示例:

void solve() {
    int n, m;
    cin >> n >> m;
    vl a(n + 1), b(m + 1);
    rep(i, 0, n) cin >> a[i];
    rep(i, 0, m) cin >> b[i];
    vl c = convolution(a, b);
    rep(i, 0, n + m) cout << c[i] << " \n"[i == n + m];
}

输入:
2 1
1 2 3
4 5

输出:
4 13 22 15
*/

constexpr int MX = 4e5 + 5;
ll F[MX];      // 预处理阶乘
ll INV_F[MX];  // 预处理逆元
ll mul(ll x, ll y) { return x * y % MOD; }
ll qpow(ll x, int n) {
    ll res = 1;
    for (; n; n >>= 1) {
        if (n % 2) res = res * x % MOD;
        x = x * x % MOD;
    }
    return res;
}
auto init = [] {
    F[0] = 1;
    for (int i = 1; i < MX; i++) F[i] = F[i - 1] * i % MOD;  // 预处理阶乘
    INV_F[MX - 1] = qpow(F[MX - 1], MOD - 2);
    for (int i = MX - 1; i; i--) {
        INV_F[i - 1] = INV_F[i] * i % MOD;
    }  // 预处理逆元
    return 0;
}();
// 计算C(n,m),即从n个数中取m个数
ll comb(int n, int m) { return m < 0 || m > n ? 0 : F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD; }
void solve() {
    ll n;
    cin >> n;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vl pre(n + 1);
    rep(i, 1, n) pre[i] = pre[i - 1] ^ a[i - 1];
    ll tot = pre[n];
    vl c(n + 1);
    rep(i, 0, n) c[i] = mul(F[2 * i], mul(INV_F[i], INV_F[i + 1]));
    vl d(n + 1);
    rep(i, 1, n - 1) d[i] = mul(c[i], c[n - i]);
    ll ans = 0;
    ll tot2 = 0;
    rep(i, 1, n - 1) tot2 = (tot2 + mul(d[i], n + 1 - i)) % MOD;
    rep(i, 0, 17) {
        if ((tot >> i) & 1) {
            ans = (ans + mul(qpow(2, i), tot2)) % MOD;
            continue;
        }
        vl x(n + 1);
        rep(j, 0, n) x[j] = ((pre[j] >> i & 1) ? MOD - 1 : 1);
        auto y = x;
        ranges::reverse(y);
        vl cv = convolution(x, y);
        ll tem = tot2;
        rep(j, 1, n - 1) tem = (tem - mul(d[j], cv[n - j]) + MOD) % MOD;
        ans = (ans + mul(qpow(2, i), tem)) % MOD;
    }
    cout << ans << endl;
    return;
}