[lnsyoj2085] 底垫

题意

给出 \(n\) 个区间 \([l_i, r_i)\)\(m\) 个询问,每次询问给出一个询问区间 \([L_i, R_i]\),要求求出随机选取 \(ll,rr\) 使得 \(L_i \le ll \le rr \le R_i\),第 \(ll\) 到第 \(rr\) 个询问区间的区间并的大小的期望。

sol

注: 下文所述区间中,小写字母表示给定的值或区间(值域为 \([1,10^9]\)),大写字母为给出的询问点或询问区间(值域为 \([1,n]\));为简化问题,题中所述左闭右开区间均改为闭区间

容易发现给定 \(L_i, R_i\) 后,存在的询问区间个数为 \(\dfrac{(L_i - R_i + 1)(L_i - R_i + 2)}{2}\) 个,因此本题是假期望,求出 \([L_i,R_i]\) 中所有子询问区间的答案之和即可,这个问题有点困难,先来考虑简化版本:给出 \(n\) 个区间 \([l_i, r_i]\)\(m\) 个询问,每次询问给出一个询问区间 \([L'_i, R'_i]\),要求求出第 \(L'_i\) 到第 \(R'_i\) 个区间的区间并的大小

可以记录值域中每个点会对哪段区间产生贡献,即对于每个点记录最后一次被第几个区间覆盖。当修改时,记原值为 \(T\),新值为 \(I\),那么对于所有右端点在 \(I\) 右侧(包括 \(I\))的询问区间,左端点为 \((T,I]\) 中的所有询问都可以增加 \(1\) 的贡献,使用线段树/树状数组维护即可。这个解法是正确的,但是它有一个问题:值域为 \(10^9\),暴力维护是不可接受的;仔细观察发现,\(I\)\(T\) 相同的部分会形成连续段,并且可以一起维护,一起造成大小为连续段长度的贡献。因此,我们需要一个快速维护连续段的数据结构,即珂朵莉树。珂学家上线了(bushi 这东西嘎嘎简单,因此出门左转 OI-Wiki

再来考虑子区间并的和:方法和刚才相同(使用珂朵莉树暴力维护连续段被第几个区间覆盖,并且处理这些连续段),只需要考虑贡献即可:设询问区间为 \([L,R]\),如果 \(L \in (T,I]\),那么获得贡献的区间中,左端点有 \(I-L+1\) 种,如果 \(L \in [1, T]\),那么获得贡献的区间中,左端点有 \(I-T\) 种。但无论怎样,只要 \(R \ge I\),那么右端点都有 \(R-I+1\) 种,因此产生的贡献为(记连续段长度为 \(len\)

\[\begin{cases} (I-L+1)(R-I+1)len &(L\in (T,I])\\ (I-T)(R-I+1)len &(L\in[1,T]) \end{cases} \]

由于我们在合并贡献时并不知道 \(L,R\) 的值,但是容易发现,这两个多项式都满足 \(ALR + BL + CR + D\) 的形式,并且这样的多项式的系数满足可加性,因此可以类似于 [lnsyoj2286/luoguP4458/BJOI2018]链上二次求和 维护这四项的系数,在计算的时候直接代入即可

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#define x first
#define y second

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int N = 200005, mod = 1e9 + 7;

int n, m;
int ql[N], qr[N];
int ans[N];

struct elem {
    int l, r, v;
    elem(int _l, int _r, int _v): l(_l), r(_r), v(_v){}
    bool operator< (const elem &W) const {
        return l < W.l;
    }
};
set<elem> odt;

struct Node {
    int l, r, lr, k;
    Node(){l = 0, r = 0, lr = 0, k = 0;}
    Node(int _l, int _r, int _lr, int _k): l(_l), r(_r), lr(_lr), k(_k){}
    Node operator+ (const Node &W) const {
        return {(l + W.l) % mod, (r + W.r) % mod , (lr + W.lr) % mod, (k + W.k) % mod};
    }
    Node operator- () const {
        return {-l, -r, -lr, -k};
    }
    int calc(int ll, int rr){
        return (((LL) ll * l % mod + (LL) rr * r % mod + (LL) ll * rr % mod * lr % mod + k) % mod + mod) % mod;
    }
}tr[N];

PII ranges[N];
vector<PII> queries[N];

int lowbit(int x){
    return x & -x;
}

void add(int x, Node val) {
    x = n - x + 1;
    for (; x <= n; x += lowbit(x)) tr[x] = tr[x] + val;
}

Node query(int x) {
    x = n - x + 1;
    Node ans(0, 0, 0, 0);
    for (; x; x -= lowbit(x)) ans = ans + tr[x];
    return ans;
}

auto split(int x){
    auto it = odt.lower_bound({x, 0, 0});
    if (x == it->l) return it;
    it -- ;
    int l = it->l, r = it->r, v = it->v;
    odt.erase(it);
    odt.insert({l, x - 1, v});
    return odt.insert({x, r, v}).x;
}

void assign(int l, int r, int v) {
    auto itr = split(r + 1), itl = split(l);
    odt.erase(itl, itr);
    odt.insert({l, r, v});
}

void perform(int l, int r, int v){
    auto itr = split(r + 1), itl = split(l);
    for (auto it = itl; it != itr; it ++ ) {
        int ll = it->l, rr = it->r, vv = it->v;
        int len = rr - ll + 1;
        add(v, {(LL) (v - 1) * len % mod, (LL) (v + 1) * len % mod, -len, (LL) (v + 1) * (-v + 1) % mod * len % mod});
        add(vv, -(Node) {(LL) (v - 1) * len % mod, (LL) (v + 1) * len % mod, -len, (LL) (v + 1) * (-v + 1) % mod * len % mod});
        add(vv, {0, (LL) (v - vv) * len % mod, 0, (LL) (v - vv) * (-v + 1) % mod * len % mod});
    }
}

int qpow(int a, int k, int p){
    int ans = 1;
    while (k){
        if (k & 1) ans = (LL) ans * a % p;
        a = (LL) a * a % p;
        k >>= 1;
    }
    return ans;
}

int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d%d", &ranges[i].x, &ranges[i].y), ranges[i].y -- ;
    for (int i = 1; i <= m; i ++ ){
        scanf("%d%d", &ql[i], &qr[i]);
        queries[qr[i]].push_back({ql[i], i});
    }
    odt.insert({1, 1000000001, 0});
    for (int i = 1; i <= n; i ++ ){
        int l = ranges[i].x, r = ranges[i].y;
        perform(l, r, i);
        assign(l, r, i);
        for (auto q : queries[i]) ans[q.y] = query(q.x).calc(q.x, i);
    }

    for (int i = 1; i <= m; i ++ ) {
        int len = qr[i] - ql[i] + 1;
        printf("%lld\n", (LL) ans[i] * qpow((LL) len * (len + 1) / 2 % mod, mod - 2, mod) % mod);
    }
}
posted @ 2025-07-29 10:44  是一只小蒟蒻呀  阅读(16)  评论(0)    收藏  举报