闲话 22.11.4

闲话

提前写好不闲的部分的感觉真好(
但是明天就没了 :<

啊对了
这个东西 \(\to\) 😉
是这个东西 \(\to\) ; )
这个东西 \(\to\) 😃
是这个东西 \(\to\) : )
这个东西 \(\to\) :>
是这个东西 \(\to\) : >

白鸟过河滩好听的
去听听吗?

没有可写的东西?
也不想透露个人信息(

数据结构杂题!

对这里是后五道题
光题的部分是612行(
但是其实讲解比较的少

CF173E

俱乐部有n个成员,每个成员有一定的地位和年龄,\(r_i\) 表示第 \(i\) 个人的地位,\(a_i\) 表示第 \(i\) 个人的年龄。

最近俱乐部里要举行活动,要求几个人组成一个小组,小组中必须要有一个队长,要成为队长有这样的条件:

  1. 队长在小组中的地位应该是最高的(可以并列第一),

  2. 小组中其他成员的年龄和队长的年龄差距不能超过 \(k\)

有些人想和自己亲密的人组在同一个小组,同时希望所在的小组人越多越好。比如x和y想在同一个小组,同时希望它们所在的小组人越多越好,当然,它们也必须选一个符合上述要求的队长,那么问你,要同时包含 \(x\)\(y\) 的小组,最多可以组多少人?

\(2\le n \le 10^5, 1\le k, r_i,a_i\le 10^9\)

首先可以先预处理出一个人作为队长时小组最多的人数。按年龄排序后双指针扫一遍,树状数组维护双指针区间内人的地位情况,对于每个人查询地位前缀和就是答案。\(i\) 的答案记作 \(s_i\)
然后可以确定一对人 \((x,y)\) 能选择哪些队长。不妨假设 \(a_x \le a_y\),则可以成为队长的人 \(c\) 满足 \(a_y - k \le a_c \le a_x + k\)\(r_c \ge \max(r_x, r_y)\)。无解也好判。
然后我们对询问按照 \(\max(r_x, r_y)\) 降序排序,这样只需要加入队长即可。
首先对 \(a_i, a_i + k, a_i - k\) 离散化,然后在 \(a\) 的值域上开一个线段树,每次加入队长时直接在 \(a_i\) 处插入 \(s_i\),维护区间最小值就能得到答案。

记得如果返回了一个 \(0\) 则答案是 \(-1\)
记得把元素放进单调栈。

code
#include <bits/stdc++.h>
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e5 + 10;
int n, k, q, r[N], a[N], aprf[N], aaft[N], ans[N], ret[N], lsh[N * 3], hrt, rbnd, abnd;

struct BIT {
    int Index[N];
    void add(int p, int v) { for (; p <= rbnd; p += p & -p) Index[p] += v; }
    int qry(int p) { int ret = 0; for (; p; p ^= p & -p) ret += Index[p]; return ret; }
} B;

struct capt {
    int r, a, aprf, aaft, id;
    bool operator < (const capt & b) const { return r > b.r; }
} tmp[N];
bool cmp(const capt & a, const capt & b) { return a.a < b.a; }

struct queries {
    int x, y, id;
    const int get() const { return max(r[x], r[y]); }
    bool operator < (const queries & b) const { return get() > b.get(); }
} qry[N];

struct SegmentCitrus {
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    int seg[N * 3 << 2];
    
    void upd(int p, int l, int r, int pos, int val) {
        if (l == r) { seg[p] = max(seg[p], val); return; }
        int mid = l + r >> 1; 
        if (pos <= mid) upd(ls, l, mid, pos, val);
        else upd(rs, mid+1, r, pos, val);
        seg[p] = max(seg[ls], seg[rs]);
    } void upd(int p, int v) { upd(1, 1, abnd, p, v); }

    int qry(int p, int l, int r, int L, int R) {
        if (R < l or r < L) return -1;
        if (L <= l and r <= R) return seg[p];
        int mid = l + r >> 1;
        return max(qry(ls, l, mid, L, R), qry(rs, mid+1, r, L, R));
    } int qry(int l, int r) { assert(l <= r); return qry(1, 1, abnd, l, r); }

    #undef ls
    #undef rs
} T;

signed main() {
    get(n, k); rep(i,1,n) get(r[i]); rep(i,1,n) get(a[i]);

    rep(i,1,n) lsh[++hrt] = r[i]; sort(lsh + 1, lsh + 1 + hrt); 
    hrt = unique(lsh + 1, lsh + 1 + hrt) - lsh - 1;
    rep(i,1,n) r[i] = lower_bound(lsh + 1, lsh + 1 + hrt, r[i]) - lsh; rbnd = hrt;
    hrt = 0; 
    rep(i,1,n) lsh[++hrt] = a[i], lsh[++hrt] = a[i] + k, lsh[++hrt] = a[i] - k; 
    sort(lsh + 1, lsh + 1 + hrt); hrt = unique(lsh + 1, lsh + 1 + hrt) - lsh - 1;
    rep(i,1,n) aprf[i] = lower_bound(lsh + 1, lsh + 1 + hrt, a[i] - k) - lsh,
               aaft[i] = lower_bound(lsh + 1, lsh + 1 + hrt, a[i] + k) - lsh,
               a[i] = lower_bound(lsh + 1, lsh + 1 + hrt, a[i]) - lsh;
    abnd = hrt;
    
    rep(i,1,n) tmp[i] = {r[i], a[i], aprf[i], aaft[i], i};
    sort(tmp + 1, tmp + 1 + n, cmp);
    for (int i(1), lp(1), rp(0); i <= n; ++ i) {
        while (rp < n and tmp[rp + 1].a <= tmp[i].aaft) ++ rp, B.add(tmp[rp].r, 1);
        while (lp < rp and tmp[lp].a < tmp[i].aprf) B.add(tmp[lp].r, -1), ++ lp;
        ans[tmp[i].id] = B.qry(tmp[i].r);
    }  
    sort(tmp + 1, tmp + 1 + n);

    get(q);
    rep(i,1,q) get(qry[i].x, qry[i].y), qry[i].id = i;
    sort(qry+1, qry+1+q);
    for (int i(1), rp(0); i <= q; ++ i) {
        while (rp < n and tmp[rp + 1].r >= qry[i].get()) ++ rp, T.upd(tmp[rp].a, ans[tmp[rp].id]);
        int ql = qry[i].x, qr = qry[i].y;
        if (a[ql] > a[qr]) swap(ql, qr);
        if (aprf[qr] > aaft[ql]) ret[qry[i].id] = -1;
        else ret[qry[i].id] = T.qry(aprf[qr], aaft[ql]);
        if (ret[qry[i].id] == 0) ret[qry[i].id] = -1;
    }

    rep(i,1,q) cout << ret[i] << '\n';
}



CF498D

一些国家由 \(n+1\) 个城市组成,这些城市位于一个笔直的路上,我们把按照他们在路上的顺序把这些城市编号为 \(1\sim n+1\),这样,第 \(i\) 条道路连接着第 \(i\) 和和第 \(i+1\) 个城市。每一个城市有一个拥堵值 \(a_i\),如果当前时间能被 \(a_i\) 整除,那么通过这条公路需要两分钟,否则需要一分钟。
给出每条公路的 \(a_i\),你需要进行 \(m\) 次如下操作:

  1. C x d:把第 \(x\) 条路的拥堵时刻改成 \(d\)

  2. A x y:问 \(x\)\(y\) 城市所需要的时间。

\(1\le n,q \le 10^5, 1\le a_i,d\le 6\)

首先这题没在树上。其次这题 \(a_i\) 的数据范围 lg 上没翻译出来。

我们发现一件事情:当前时间在 \(\bmod \text{ lcm}\{ 1, 2, 3, 4, 5, 6\}\) 的意义下通过一段道路的时间相等。也就是说,对于一段道路,我们只需要维护 \(60\) 个信息即可得到所有时间下通过该段道路所需时间。于是考虑线段树维护。
假设 \(f_{i,p}\)\(p\) 时刻开始行驶的情况下通过 \(i\) 节点对应道路需要的时间。不难发现

\[f_{i,p} = f_{ls,p} + f_{rs,(p+f_{ls,p}) \bmod 60} \]

也即,通过一段道路所需时间 \(=\) 通过左半段道路所需时间 \(+\) 通过完左半段道路后再通过右半段道路所需时间。这样就可以 \(\text{push up}\) 了。
节点的初始化是方便的,我们只需要查看 \(p\) 是否为 \(a_i\) 的倍数,是则为 \(2\) 反之则为 \(1\)
更新也是方便的,重新初始化再上提信息即可。
查询也是方便的,记录到达当前区间左端点时的时间即可。

于是可以在 \(O(60n\log n)\) 的复杂度内得到答案。

分块?您开心就好。
可结合性信息用为适应不可结合信息而产生的方法维护?我的评价是砂锅烤面筋。

code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e5 + 10; typedef long long ll;
int n, q, a, l, r;
char typ;

class SegmentBeats {
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    int seg[N << 2][60];

    void ps_p(int p) {
        rep(i,0,59) seg[p][i] = seg[ls][i] + seg[rs][(seg[ls][i] + i) % 60];
    }

    void build(int p, int l, int r) {
        if (l == r) {
            cin >> a; rep(i,0,59) seg[p][i] = 1 + (i % a == 0); return ;
        } int mid = l + r >> 1;
        build(ls, l, mid);
        build(rs, mid+1, r);
        ps_p(p);
    } 

    void upd(int p, int l, int r, int pos, int val) {
        if (l == r) {
            rep(i,0,59) seg[p][i] = 1 + (i % val == 0); return;
        } int mid = l + r >> 1;
        if (pos <= mid) upd(ls, l, mid, pos, val);
        else upd(rs, mid+1, r, pos, val);
        ps_p(p);
    }
    
    int qry(int p, int l, int r, int L, int R, int now) {
        if (r < L or R < l) return 0;
        if (L <= l and r <= R) return seg[p][now];
        int mid = l + r >> 1, ans = qry(ls, l, mid, L, R, now);
        return ans + qry(rs, mid+1, r, L, R, (now + ans) % 60);
    }

    #undef ls
    #undef rs

  public :

    void build() { build(1, 1, n); }
    void upd(int p, int v) { upd(1, 1, n, p, v); }
    int qry(int l, int r) { return qry(1, 1, n, l, r, 0); }
} Tr;

signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n; Tr.build(); cin >> q;
    while (q--) {
        cin >> typ >> l >> r;
        if (typ == 'C') Tr.upd(l, r);
        else cout << Tr.qry(l, r - 1) << '\n';
    }
}



CF484E

给定一个长度为 \(n\) 的数列 \(a_i\),有 \(m\) 次询问,询问形如 \((l,r,k)\),要你在区间 \([l,r]\) 内选一个长度为 \(k\) 的区间,求区间最小数的最大值。

\(1\le n,m\le 10^5, 1\le a_i\le 10^9\)

\(O(n\log^2 n)\) 做法挺sb的。
考虑二分答案,大于等于 \(\text{mid}\) 的值设为 \(1\),小于的设为 \(0\),求区间内是否有长度大于等于 \(k\)\(1\) 段。
考虑主席树,根维开在离散化数组上,值维开在序列上。从大到小顺次加入元素,然后查询 \(\text{mid}\) 的根就能所有值都大于等于 \(\text{mid}\)
维护即可。

code
#include <bits/stdc++.h>
#define int long long
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e5 + 10; typedef long long ll;
int n, q, l, r, k;

int rt[N], rk[N], a[N], val[N], mlc;
struct node {
    int len, lmax, rmax, maxv;
    node() { memset(this, 0, sizeof(node)); }
    node(int x) { len = 1, lmax = rmax = maxv = x; }
    node(int _len, int _lmax, int _rmax, int _maxv) : 
        len(_len), lmax(_lmax), rmax(_rmax), maxv(_maxv) {}
    node operator + (const node & b) const {
        return node(
            len + b.len,
            lmax == len ? lmax + b.lmax : lmax,
            b.rmax == b.len ? b.rmax + rmax : b.rmax,
            max( {maxv, b.maxv, rmax + b.lmax} )
        );
    }
};

struct SegmentBeats {
    node v; 
    int ls, rs;
    #define v(p) seg[p].v
    #define ls(p) seg[p].ls
    #define rs(p) seg[p].rs
} seg[N << 5];

void build(int & p, int l, int r) {
    if (l == r) {
        v(p) = node(0);
        return ;
    } int mid = l + r >> 1;
    build(ls(p), l, mid);
    build(rs(p), mid+1, r);
    v(p) = v(ls(p)) + v(rs(p));
}

int insert(int prot, int l, int r, int pos) {
    int p = ++ mlc; seg[p] = seg[prot];
    if (l == r) {
        v(p) = node(1);
        return p;
    } int mid = l + r >> 1;
    if (pos <= mid) ls(p) = insert(ls(prot), l, mid, pos);
    else rs(p) = insert(rs(prot), mid+1, r, pos);
    v(p) = v(ls(p)) + v(rs(p));
    return p;
}

node qry(int p, int l, int r, int L, int R) {
    if (L <= l and r <= R) return v(p);
    int mid = l + r >> 1; node ret;
    if (L <= mid) ret = ret + qry(ls(p), l, mid, L, R);
    if (mid < R) ret = ret + qry(rs(p), mid+1, r, L, R);
    return ret;
}

int query(int L, int R, int k) {
    int l = 1, r = n, mid;
    while (l <= r) {
        mid = l + r >> 1;
        if (qry(rt[mid], 1, n, L, R).maxv < k) l = mid + 1;
        else r = mid - 1;
    } return r + 1;
}

signed main() {
    get(n); rep(i,1,n) get(a[i]), val[i] = a[i], rk[i] = i; get(q); 
    sort(val + 1, val + 1 + n, [&](int x, int y){ return x > y; });
    sort(rk + 1, rk + 1 + n, [&](int x, int y) { return a[x] > a[y]; });
    build(rt[0], 1, n);
    rep(i,1,n) rt[i] = insert(rt[i-1], 1, n, rk[i]);
    rep(i,1,q) {
        get(l, r, k);
        cout << val[query(l, r, k)] << '\n';
    }
}



CF1677E

给定一个长度为 \(n\) 的排列 \(p\)。定义一个区间 \([l,r]\) 是美丽的当且仅当该区间中存在两个 \(i,j\) 满足 \(l\leq i<j\leq r\),且有 \(a_ia_j=\max\limits_{k=l}^ra_k\)

现有 \(q\) 组询问,每组询问给定区间 \([l_i,r_i]\),请你求出其有多少个子区间是美丽的。

\(1\leq n\leq2\times10^5\)\(1\leq q\leq 10^6\)

看到 \(\max\) 想到单调栈找到任意元素左右第一个大于它的位置,对于 \(i\) 记作 \(l_i,r_i\)
然后考虑一个贡献问题。令 \(s_i\)\(i\) 元素所在位置,并记美丽的区间 \([l,r]\) 指代了 \(l\)\(r\) 配对。
仍然是枚举 \(a_i\) 的因子,设两个因子为 \(p,q\)。如果 \(l_i < s_p \le i \le s_q < r_i\),则任意 \(s_q\le r < r_i\) 的位置都可以作为右端点和 \(l_i < l \le s_p\) 的左端点配对。如果 \(s_p,s_q \le i\)\(i\le s_p,s_q\) 基本同理。我们只需要得到左右两侧可能可行的范围即可。
特化对左侧的枚举。我们首先处理出能够配对的区间范围,然后扫描左侧能够配对的区间,对左侧区间内每个元素找到右侧能与他配对的范围,保存在这个元素对应的区间集合内。
随后我们从右到左扫描线。当扫描到一个元素时在能和它配对的所有范围内分别加 \(1\) 表示以当前点或更右侧点为左端点的话取该范围内点为右端点是可行的。维护即可。
但是我们发现这样产生的区间是 \(O(n^2)\) 的。考虑启发式。每次扫描更小的一侧,这样枚举次数是 \(O(n\log n)\) 的。从左右两侧分别扫描线即可。

时间复杂度 \(O(n\log^2 n)\)

code
#include <bits/stdc++.h>
#define int long long
using namespace std;
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e6 + 10; typedef long long ll;
int n, q, l, r, a[N], lp[N], rp[N], pos[N], ans[N];
int stk[N], top;
vector <pair<int,int> > d1[N], d2[N];
struct queries {
    int l, r, id;
} qy[N];

class SegmentCitrus {

  private : 

    #define ls (p << 1)
    #define rs (p << 1 | 1)
    #define sum(p) seg[p].sum
    #define siz(p) seg[p].size
    #define lzy(p) seg[p].lazy
    struct node {
        int size, lazy, sum;
    } seg[N << 2];

    void ps_d(int p) {
        if (lzy(p) == 0) return;
        sum(ls) += siz(ls) * lzy(p), sum(rs) += siz(rs) * lzy(p);
        lzy(ls) += lzy(p), lzy(rs) += lzy(p);
        lzy(p) = 0; return ;
    }

    void ps_p(int p) { sum(p) = sum(ls) + sum(rs); }

    void build(int p, int l, int r) {
        siz(p) = r - l + 1, lzy(p) = sum(p) = 0;
        if (l == r) return;
        int mid = l + r >> 1;
        build(ls, l, mid);
        build(rs, mid+1, r);
    }

    void add(int p, int l, int r, int L, int R, int v) {
        if (L <= l and r <= R) {
            sum(p) += v * siz(p);
            lzy(p) += v;
            return;
        } int mid = l + r >> 1; ps_d(p);
        if (L <= mid) add(ls, l, mid, L, R, v);
        if (mid < R) add(rs, mid+1, r, L, R, v);
        ps_p(p);
    }

    int qry(int p, int l, int r, int L, int R) {
        if (L <= l and r <= R) return sum(p);
        int mid = l + r >> 1, ret = 0; ps_d(p);
        if (L <= mid) ret += qry(ls, l, mid, L, R);
        if (mid < R) ret += qry(rs, mid+1, r, L, R);
        return ret;
    }
    
    #undef ls
    #undef rs
    #undef sum
    #undef siz
    #undef lzy

  public : 

    void clear() { build(1, 1, n); }
    void update(int l, int r, int v) { add(1, 1, n, l, r, v); }
    int query(int l, int r) { return qry(1, 1, n, l, r); }

} Tr;

signed main() {
    get(n, q); rep(i,1,n) get(a[i]), pos[a[i]] = i; a[0] = a[n + 1] = n + 1;
    rep(i,1,q) get(qy[i].l, qy[i].r), qy[i].id = i;

    stk[top = 1] = 0;
    rep(i,1,n) {
        while (top and a[stk[top]] < a[i]) -- top;
        lp[i] = stk[top]; stk[++top] = i;
    } 
    stk[top = 1] = n + 1;
    pre(i,n,1) {
        while (top and a[stk[top]] < a[i]) -- top;
        rp[i] = stk[top]; stk[++top] = i;
    }

	rep(i,1,n) {
		if (rp[i] - i <= i - lp[i]) {
			r = lp[i];
			for (int j = 1; j * j <= a[i]; ++j) {
				if (a[i] % j != 0) continue;
				int fx = pos[j], fy = pos[a[i] / j];
				if (fx == fy) continue;
				if (fx > fy) swap(fx, fy);
				if (fx > lp[i] && fy <= i) r = max(r, fx);
			}
			for (int j = i; j < rp[i]; ++j) {
				if (a[i] % a[j] == 0) {
					int nw = a[i] / a[j];
					if (pos[nw] == j) continue;
					if (pos[nw] > lp[i] && pos[nw] < j) {
						r = max(r, pos[nw]);
						r = min(r, i);
					}
				}
				if (lp[i] < r) d1[j].push_back({lp[i] + 1, r});
			}
		} else {
			l = rp[i];
			for (int j = 1; j * j <= a[i]; ++j) {
				if (a[i] % j != 0) continue;
				int fx = pos[j], fy = pos[a[i] / j];
				if (fx == fy) continue;
				if (fx > fy) swap(fx, fy);
				if (fy < rp[i] && fx >= i) l = min(l, fy);
			}
			for (int j = i; j > lp[i]; --j) {
				if (a[i] % a[j] == 0) {
					int nw = a[i] / a[j];
					if (pos[nw] == j) continue;
					if (pos[nw] > j && pos[nw] < rp[i]) {
						l = min(l, pos[nw]);
						l = max(l, i);
					}
				}
				if (l < rp[i]) d2[j].push_back({l, rp[i] - 1});
			}
		}
	}

    sort(qy+1, qy+1+q, [&](queries x, queries y){ return x.r < y.r; });
    Tr.clear();
    for (int i(1), p(1); i <= n; ++ i) {
        for (auto [l, r] : d1[i]) Tr.update(l, r, 1);
        while (p <= q and qy[p].r == i) ans[qy[p].id] += Tr.query(qy[p].l, qy[p].r), ++ p;
    }

    sort(qy+1, qy+1+q, [&](queries x, queries y){ return x.l > y.l; });
    Tr.clear();
    for (int i(n), p(1); i > 0; -- i) {
        for (auto [l, r] : d2[i]) Tr.update(l, r, 1);
        while (p <= q and qy[p].l == i) ans[qy[p].id] += Tr.query(qy[p].l, qy[p].r), ++ p;
    }

    rep(i,1,q) cout << ans[i] << '\n';
}



CF1034D

\(n\) 个区间 \([a_i,b_i]\) 。定义区间的区间 \([l,r]\) 的价值是第 \(l\) 个区间到第 \(r\) 个区间的并的长度,找出 \(k\) 个不同的区间的区间,使得总价值最大。

\(n\le 3\times 10^5, k \le \min\{\frac{n(n-1)}2, 10^9\}\)

首先考虑给定 \(q\) 个二层区间,求他的价值。这是很简单的,使用扫描线扫每个区间,\(l\) 节点维护 \([l,i]\) 二层区间的答案。顺序加入区间,每个位置记录最后一次覆盖它的区间 \(i'\),当 \(i\) 覆盖它时在 \([i' + 1,i]\) 范围内加 \(1\)。这个可以珂朵莉树维护。复杂度 \(O(n\log n)\)。具体这里

考虑二分第 \(k\) 个区间并的长度。然后问题转化为如何快速求得大于 \(\text{mid}\) 长度的区间数和区间和。
考虑继续顺次加入区间。设区间的区间 \([l,r]\) 的答案为 \(v(l,r)\)。容易发现 \(v(l,r) > v(l+1,r), v(l,r) > v(l,r-1)\)。因此,若固定 \(r\) 不变,\(v(i,r)\) 关于 \(i\) 单调递减。
我们设满足 \(v(i,r) \ge \text{mid}\)\(i\) 的数量为 \(T(r)\)。则我们需要快速求得 \(\sum T(i)\)
平凡思路可以通过线段树上二分在 \(O(n\log^2n)\) 的复杂度内得到。然后时间复杂度炸了。
考虑 \(v(i,r)\) 单减,我们便可以指针法扫出每个点是否能加入到满足 \(T()\) 的集合内。预处理顺次加入区间时需要做的区间加,然后差分维护即可。

时间复杂度 \(O(n\log n)\)

code
#include <bits/stdc++.h>
using namespace std;
#define int long long
template<typename T> void get(T & x) {
	x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
	while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x); 
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 3e5 + 10, mod = 1e9 + 7;
int n, k, ans, delt[N];
pair<int,int> a[N];

struct Chtholly_Node {
    mutable int l, r, v;
    bool operator < (const Chtholly_Node & b) const { if (l == b.l) return r < b.r; return l < b.l; }
}; set <Chtholly_Node> cht;
using iter = set <Chtholly_Node> :: iterator;

iter split(int k) {
    iter it = cht.lower_bound({k, 0, 0});
    if (it != cht.end() and it->l == k) return it;
    -- it; if (it->r < k) return cht.end();
    auto x = *it; cht.erase(it);
    cht.insert( {x.l, k, x.v} );
    return cht.insert( {k, x.r, x.v} ).first;
}

struct change {
    int pos, val; 
    bool operator < (const change & v) const { return pos < v.pos; }
}; vector <change> vec[N];

bool check(int val) {
    memset(delt, 0, sizeof(decltype(*delt)) * (n + 2));
    int now = 0, sum = 0, cnt = 0, tot = 0;
    for (int l(1), r(1); r <= n; ++ r) {
        now += a[r].second - a[r].first;
        sum += 1ll * l * (a[r].second - a[r].first);
        delt[r + 1] = -(a[r].second - a[r].first);
        for (auto x : vec[r]) {
            if (x.pos < l) sum -= 1ll * x.val * x.pos;
            else {
                now -= x.val;
                sum -= 1ll * l * x.val;
                delt[x.pos + 1] += x.val;
            }
        } 
        while (now >= val) now += delt[++ l], sum += now;
        cnt += l - 1, tot += sum - now;
    } if (cnt < k) return false;
    ans = tot - 1ll * val * (cnt - k);
    return  true;
}

signed main() {
    get(n, k); rep(i,1,n) get(a[i].first, a[i].second); cht.insert( {1, 1000000000, 0} );
    rep(i,1,n) {
        iter itr = split(a[i].second), itl = split(a[i].first);
        for (auto it = itl; it != itr; it = cht.erase(it)) vec[i].push_back( {it->v, it->r - it->l} );
        sort(vec[i].begin(), vec[i].end()); cht.insert( {a[i].first, a[i].second, i} );
    } 
    int l = 0, r = 1e9, mid;
    while (l <= r) {
        int mid = l + r >> 1;
        if (check(mid)) l = mid + 1;
        else r = mid - 1;
    }
    cout << ans << '\n';
}
posted @ 2022-11-04 17:44  joke3579  阅读(69)  评论(0编辑  收藏  举报