1 条题解

  • 0
    @ 2026-4-25 1:09:05

    这题如果不带修有一个 O(nV)O(nV) 的倍增做法,但是其实和正解没啥关系。

    考虑将相同的数缩成若干个连续段 (x1,y1),(x2,y2),,(xk,yk)(x_1, y_1), (x_2, y_2), \ldots, (x_k, y_k)

    如果出现了一个谷 xi1>xi<xi+1x_{i - 1} > x_i < x_{i + 1},那么我们要把两边并起来,至少要把 xix_i 变成 min(xi1,xi+1)\min(x_{i - 1}, x_{i + 1})

    d=min(xi1,xi+1)xid = \min(x_{i - 1}, x_{i + 1}) - x_i。如果 2dyi2^d \mid y_i,那么我们可以把 xix_i 提升到 min(xi1,xi+1)\min(x_{i - 1}, x_{i + 1}),和两边其中一个合并。

    否则两边不可能并起来,把 (xi,yi)(x_i, y_i) 分给两边然后继续做,相当于把 (xi,yi)(x_i, y_i) 删除,然后插入 $(\min(x_{i - 1}, x_{i + 1}), \left\lfloor\frac{y_i}{2^d}\right\rfloor), (+\infty, 1), (\min(x_{i - 1}, x_{i + 1}), \left\lfloor\frac{y_i}{2^d}\right\rfloor)$。

    这样整个序列就变成了一个单峰序列,连续段个数是 O(V)O(V) 的。在消除谷的过程中可以把 xi+log2yix_i + \left\lfloor\log_2 y_i\right\rfloor 计入答案。求答案就在序列两端分别添加一个 ++\infty 然后不停做上面的过程即可。

    发现这个信息是可以合并的,所以可以上线段树支持单点修改。合并两个信息就把右边信息的所有连续段逐个加入到左边信息中。一个连续段添加到一个信息中,就看有没有新产生谷,如果产生了就递归加入新的连续段。

    时间复杂度 O(nVlogn)O(nV \log n),空间复杂度 O(nV)O(nV)

    以下的代码在 QOJ 同时获得了最短解和最优解。

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, N;
    
    struct node {
    	int t, k, a[22][2];
    	
    	node(int x = 1e9) {
    		t = (x < 1e8 ? x : 0);
    		a[k = 1][0] = x;
    		a[k][1] = 1;
    	}
    	
    	void add(int x, int y) {
    		if (!y) {
    			return;
    		}
    		while (k >= 2 && a[k - 1][0] > a[k][0] && a[k][0] < x) {
    			int p = a[k][0], q = a[k][1];
    			if (a[--k][0] > 1e8 && x > 1e8) {
    				continue;
    			}
    			int d = min(a[k][0], x) - p;
    			add(p + d, q >> d);
    			if (q & ((1 << d) - 1)) {
    				add(1e9, 1);
    				add(p + d, q >> d);
    			}
    		}
    		if (k && a[k][0] == x) {
    			a[k][1] += y;
    		} else {
    			a[++k][0] = x;
    			a[k][1] = y;
    		}
    		if (a[k][0] < 1e8) {
    			t = max(t, a[k][0] + __lg(a[k][1]));
    		}
    	}
    } a[262199];
    
    inline node operator + (node a, node b) {
    	a.t = max(a.t, b.t);
    	for (int i = 1; i <= b.k; ++i) {
    		a.add(b.a[i][0], b.a[i][1]);
    	}
    	return a;
    }
    
    void prepare_game(vector<int> b) {
    	n = (int)b.size();
    	N = 1 << (__lg(n + 1) + 1);
    	for (int i = 1; i <= n; ++i) {
    		a[i + N] = node(b[i - 1]);
    	}
    	for (int i = N; i; --i) {
    		a[i] = a[i << 1] + a[i << 1 | 1];
    	}
    }
    
    int play_game(int l, int r) {
    	node u(1e9), v(1e9);
    	for (l += N, r += N + 2; l ^ r ^ 1; l >>= 1, r >>= 1) {
    		if (!(l & 1)) {
    			u = u + a[l ^ 1];
    		}
    		if (r & 1) {
    			v = a[r ^ 1] + v;
    		}
    	}
    	return (u + v).t;
    }
    
    void update_game(int x, int y) {
    	a[x += N + 1] = node(y);
    	while (x >>= 1) {
    		a[x] = a[x << 1] + a[x << 1 | 1];
    	}
    }
    
    • 1

    信息

    ID
    7403
    时间
    4000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者