• LOJ #6270. 数据结构板子题 (离线+树状数组)


    题意

    (n) 个区间,第 (i) 个区间是 ([l_i,r_i]) ,它的长度是 (r_i-l_i)

    (q) 个询问,每个询问给定 (L,R,K) ,询问被 ([L,R]) 包含的且长度不小于 (K) 的区间数量。

    (n,q≤500,000)

    题解

    想了无数种 (O((n+q) log^2 n)) 的做法啊TAT 后来看了 这份代码 后恍然大悟 .

    这题一个很显然的想法是离线 qwq

    首先离线 (l ~or~ r) 似乎不太可行 , 因为要动态支持查找一个区间 ([L, R]) 不小于 (K) 的个数 , 而主席树需要离线完成 (划分树没学过 , 不知道可不可以) 在线的话只有 (O(log^2)) 的复杂度可行了 .

    那继续考虑离线 (K) , 然后这样的话 , 我们就可以忽略 (K) 的限制 .

    假设我们当前算出了对于一个询问 所有长度 (le k) 的区间个数 (res_k) , 那这个答案就可以表示成 (res_n - res_{K-1}) .

    我们此时只需要做的就是 计算一个区间包含了当前的多少个区间 .

    这个如何做呢 qwq

    两个区间 (A, B) 只有三种情况 .

    1. (A subseteq B) (此时 (A) 可以等于 (B) ) , (B) 完全包含 (A) , (|B| ge |A|)
    2. (A ot subseteq B)(B ot subseteq A) , 此时 (A,B) 交的部分不会是这两个区间中任意一个全集 .
    3. (B subseteq A ~(A ot = B)) , (A) 完全包含 (B) , (|A| > |B|)

    我们假设前面插入的区间为 (A) , 当前询问的区间为 (B) . 我们计算的就只有第 (1) 种情况了 .

    不难发现 第 (3) 情况计数很麻烦 .

    我们最好忽略第 (3) 种情况 , 只计算第 (2) 种情况 . (因为这个 (A) 会有两个出去不好计算)

    不难发现长度会有限制 , 那我们询问的时候 只要询问所有 (len le R - L) 的区间就行了 , 也就是每个离线后变成计算 (res_{R-L} - res_{K - 1}) .

    而对于第 (2) 种情况 , (A) 只有一端会超出 (B) .

    然后每次询问 (res_i) 的时候 假设当前插入的线段的总数是 (tot) .

    答案显然就是 $tot - $ 前面左端点存在于 ([1, L - 1]) 的线段个数 - 前面右端点存在于 ([R + 1, n]) 的线段个数 .

    (可以发现 , 这样很好地处理了两个线段相离的情况)

    然后这个用两个树状数组统计一下 , 左端点和右端点各一个 .

    所以最后的时间复杂度就是 (O((n + q) log n)) 了 .

    注意一开始离线的时候 要特判掉 (R-L < K) 的情况 !!

    最好自己画图理解 , 博主懒就没放上来了qwq

    代码

    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define debug(x) cout << #x << ':' << x << endl
    using namespace std;
    
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
        return x * fh;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("6270.in", "r", stdin);
    	freopen ("6270.out", "w", stdout);
    #endif
    }
    
    const int N = 500100;
    
    int n, q;
    
    #define lowbit(x) (x & -x)
    struct Fenwick_Tree {
    	int sumv[N];
    
    	inline void Update(int pos) { for(; pos <= n; pos += lowbit(pos)) ++ sumv[pos]; }
    
    	inline int Query(int pos) { int res = 0; for (; pos > 0; pos ^= lowbit(pos)) res += sumv[pos]; return res; }
    } Pre, Suf;
    
    struct Ask { int opt, l, r, id; } ;
    typedef pair<int, int> PII;
    #define fir first
    #define sec second
    
    vector<Ask> Q[N]; vector<PII> V[N]; int ans[N];
    
    #define Rev(x) (n - (x) + 1)
    int main () {
    	File();
    	n = read(); q = read();
    
    	For (i, 1, n) {
    		int l = read(), r = read(), len = r - l;
    		V[len].push_back(make_pair(l, r));
    	}
    
    	For (i, 1, q) {
    		int l = read(), r = read(), k = read(), len = r - l;
    		if (len >= k)
    			Q[k - 1].push_back((Ask) {-1, l, r, i}),
    			Q[len].push_back((Ask) {1, l, r, i});
    	}
    
    	int tot = 0;
    	For (i, 1, n) {
    		for (PII Up : V[i])
    			Pre.Update(Up.fir), Suf.Update(Rev(Up.sec)), ++ tot;
    		for (Ask Rp : Q[i])
    			ans[Rp.id] += Rp.opt * (tot - Pre.Query(Rp.l - 1) - Suf.Query(Rev(Rp.r + 1)));
    	}
    
    	For (i, 1, q) printf ("%d
    ", ans[i]);
    
        return 0;
    }
    
  • 相关阅读:
    C#.NET常见问题(FAQ)-如何在不同窗体之间传递值
    C#.NET常见问题(FAQ)-如何不显示窗口的关闭按钮
    C#.NET常见问题(FAQ)-如何判断两个类是否相同类型
    C#.NET常见问题(FAQ)-如何判断某个字符是否为汉字
    C#.NET常见问题(FAQ)-如何改变字符串编码
    C# 多线程编程 ThreadStart ParameterizedThreadStart
    C# 线程调用主线程中的控件
    LINQ to XML 编程基础
    LINQ to XML 建立,读取,增,删,改
    WinForm 自动完成控件实例代码简析
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9189014.html
Copyright © 2020-2023  润新知