线段树实现区间除和开方

区间除和开方要解决的就是以下两种操作:

  1. \(\forall i \in [l, r], \lfloor \frac{a_i}{d} \rfloor \to a_i\)
  2. \(\forall i \in [l, r], \lfloor \sqrt{a_i} \rfloor \to a_i\)

正常的标记操作是很难实现这些操作的,然而我们可以把这些操作进行一些转换,因为这些操作在一定的值域范围内是可以批量处理的。换句话说,如果区间内极差小,那么除法或开方的效果在区间内等效,所以可以转化成减法来进行实现。

拿除法举例:在区间\((l, r)\)中,如果\( \max(S) – \lfloor \frac{\max(S)}{d} \rfloor = \min(S) – \lfloor \frac{\min(S)}{d} \rfloor \),那么我们就可以把这个差值作为减数,用正常的加减方式进行标记。

继续阅读线段树实现区间除和开方

笛卡尔树

性质

节点内子树的值都比本身要小(堆的性质),且整个子树在序列上是一段连续的子串。

建树方式和例题

HDU-1506 单调栈的裸题。

// HDU-1506.cpp
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int MAX_N = 1e5 + 200;

struct node
{
    int ch[2], idx, fa, val;
    void clear() { ch[0] = ch[1] = idx = fa = val = 0; }
} nodes[MAX_N];

int n, stk[MAX_N << 1], top;
ll ans;

int build()
{
    for (int i = 1; i <= n; i++)
    {
        int k = i - 1;
        while (nodes[k].val > nodes[i].val)
            k = nodes[k].fa;
        nodes[i].ch[0] = nodes[k].ch[1];
        nodes[k].ch[1] = i;
        nodes[i].fa = k, nodes[nodes[i].ch[0]].fa = i;
    }
    return nodes[0].ch[1];
}

int dfs(int u)
{
    if (u == 0)
        return 0;
    int siz = 1;
    for (int bit = 0; bit < 2; bit++)
        siz += dfs(nodes[u].ch[bit]);
    ans = max(ans, 1LL * siz * nodes[u].val);
    return siz;
}

int main()
{
    while (scanf("%d", &n) && n != 0)
    {
        ans = 0;
        nodes[0].ch[0] = nodes[0].ch[1] = nodes[0].idx = nodes[0].fa = nodes[0].val = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &nodes[i].val), nodes[i].idx = i, nodes[i].fa = 0;
            memset(nodes[i].ch, 0, sizeof(nodes[i].ch));
        }
        dfs(build());
        printf("%lld\n", ans);
    }
    return 0;
}

Educational Codeforces Round 71 Div.2 解题报告 (CF1207)

D – Numbers of Permutations

思考量很小的一道题。

先用间接法来算。初始答案为\(n!\),然后考虑容斥掉那些符合排序规则的方案。发现,我们可以分开来考虑,按照容斥的顺序:先考虑只算第一维、第二维的,在加回来两维都考虑的。

第一维和第二维的做法非常显然:排序之后用多重集的排列就行了,考虑每一个相同的区间里面内部乘起来。如果有\(k\)个连续的数段,那么部分的答案就是\(\prod_{i = 1}^l len_k!\)。

继续阅读Educational Codeforces Round 71 Div.2 解题报告 (CF1207)

LCT 的一类题目

LCT 简述

LCT (Link Cut Tree) 是一种维护树链的灵活的数据结构。与线段树不同的是,LCT 一般使用 Splay 这种非常优雅、灵活的数据结构来维护树链。因为 LCT 支持动态增边、删边,所以很多题目就可以打破一些限制进行求解。

继续阅读LCT 的一类题目

[Fortuna OJ]Aug 5th – Group A 解题报告

A – 矩阵游戏

这道题看了题解之后发现就是一道 sb 题。

考虑将每一列看成一个数,发现如果忽略掉列的操作,这些数仍然满足等差的条件,所以我们只要暴力算完第一列就可以进行等差了。如果考虑列的操作,那么我们就大力暴力就行了,时间复杂度是\(O(n + m)\)。

继续阅读[Fortuna OJ]Aug 5th – Group A 解题报告

[Fortuna OJ]Aug 4th – Group A 解题报告

今天被各路神仙吊打,顺利 gg。

A – Forging 锻造

在考场上我推了个有后效性的 DP,还以为是高斯消元,然后看到数据范围就 GG 了。这道题主要是把一些东西给转换掉了。

首先,对一个概率为\(p\)的事件,它的期望次数是\(\frac{1}{p}\)(参见这里)。然后,考虑一个状态\(dp[i]\)代表合成第\(i\)级武器的期望花费。显然\(dp[0] = a\)。

考虑\(dp[1]\)的求法,首先需要两个级别为\(0\)的武器,然后发现无论失败与否都可以保证至少又一个\(0\)级武器,所以我们只需要注意另外一个武器的花费就可以了:

\[ dp[1] = dp[0] + dp[1] \times \frac{1}{p} \]

继续阅读[Fortuna OJ]Aug 4th – Group A 解题报告