Loading

:D 获取中...

Note - OI tricks | 动态更新

🌹:不知道这个是不是应该放到 Essay 里。其实这里更偏实现一点,不知道算不算 trick?

  • 线段树维护 Hash。

    这个左右区间是很好合并的,主要是提出来说一下,因为每次遇到都只会搜索。我指的是百度 / Google。

    下面是一个维护正序和逆序 Hash 的板子。

    using ull = unsigned long long;
    
    ull base[N];
    
    struct segment_tree {
    	int l, r;
    	ull hash1, hash2;
    } t[N << 2];
    
    void push_up(int p) {
    	int mid = t[p].l + t[p].r >> 1;
    	t[p].hash1 = (t[p << 1].hash1 * base[t[p].r - mid] + t[p << 1 | 1].hash1);
    	t[p].hash2 = (t[p << 1 | 1].hash2 * base[mid - t[p].l + 1] + t[p << 1].hash2);
    }
    
    void build(int p, int l, int r) {
    	t[p].l = l, t[p].r = r;
    	if (l == r) {
    		t[p].hash1 = t[p].hash2 = s[l] - 'a' + 1;
    		return;
    	}
    	int mid = l + r >> 1;
    	build(p << 1, l, mid);
    	build(p << 1 | 1, mid + 1, r);
    	push_up(p);
    }
    
    void update(int p, int x, char c) {
    	if (t[p].l == t[p].r) {
    		t[p].hash1 = t[p].hash2 = c - 'a' + 1;
    		return;
    	}
    	int mid = t[p].l + t[p].r >> 1;
    	if (x <= mid) update(p << 1, x, c);
    	else update(p << 1 | 1, x, c);
    	push_up(p);
    }
    
    ull ask1(int p, int l, int r) {
    	if (l <= t[p].l && t[p].r <= r) return t[p].hash1;
    	int mid = t[p].l + t[p].r >> 1;
    	ull ans = 0;
    	if (l <= mid) ans += ask1(p << 1, l, r) * base[max(min(r, t[p].r) - mid, 0)];
    	if (r > mid) ans += ask1(p << 1 | 1, l, r);
    	return ans;
    }
    
    ull ask2(int p, int l, int r) {
    	if (l <= t[p].l && t[p].r <= r) return t[p].hash2;
    	int mid = t[p].l + t[p].r >> 1;
    	ull ans = 0;
    	if (l <= mid) ans += ask2(p << 1, l, r);
    	if (r > mid) ans += ask2(p << 1 | 1, l, r) * base[max(mid - max(l, t[p].l) + 1, 0)];
    	return ans;
    }
    
  • bitset,\(O(n) \to O(\dfrac{n}{w})\) 的利器。

    一般加速维护 bool 数组与起来、或起来等操作。 Solution - Walk the Runway

    本日见到了线段树维护 bitset,可以跑过 \(O(\frac{mn \log n}{w})\),其中 \(n \leq 2 \times 10 ^ 4\)\(m \leq 10 ^ 3\)

  • 莫队维护区间 mex。


    小常数的 \(O(n \sqrt{n} \log n)\)。看代码秒懂:

    void add(int x, int k) {
    	for (; x <= n; x += x & -x) bit[x] += k;
    }
    
    int ask(int x) {
    	int sum = 0;
    	for (; x; x -= x & -x) sum += bit[x];
    	return sum;
    }
    
    void add(int x) {
    	if (!sum[a[x]]) add(a[x], 1);
    	sum[a[x]]++;
    }
    
    void del(int x) {
    	sum[a[x]]--;
    	if (!sum[a[x]]) add(a[x], -1); 
    }
    

    然后判断就是 ask(q[i].id) == q[i].id - 1q[i].id 是值。

    哦哦,以及可以用回滚莫队维护,但是那玩意容易写错,所以就这样了。

  • 高维前缀和。

    其实很简单,设已知 \(f_S\)\(S \subset U\),要求出所有 \(g_{S} = \sum_{I \subset S} f_I\)

    初始 \(g_S = f_S\),然后转移枚举一下不取某元素 \(x \in S\) 的情况 \(g_S = \sum\limits_{x \in S} g_{S \setminus x}\),很好感性理解,这样加起来就是 \(S\) 的所有子集啦。时间复杂度 \(O(n \times 2 ^ n)\)。题目咕咕咕。

  • 树状数组 \(O(\log n)\) 二分。

    远古博文

  • 对于和的平均值(形式化地,\(\bar a = \dfrac{\sum_{i = 1} ^ n a_i}{n}\)),可以转化成 \(a_i - \bar a\) 然后和 \(0\) 判断大小等。

  • 对于第 \(k\) 大 / 小,转化为二分判断 \(\leq mid\)\(\geq mid\) 的个数与 \(k\) 的大小比较。e.g. 中位数。

  • 如果一个区间操作很有规律性,可以考虑转换差分。典型的比如加一个等差数列。

posted @ 2024-01-19 21:53  liuzimingc  阅读(32)  评论(0编辑  收藏  举报