zoukankan      html  css  js  c++  java
  • CF1034D Intervals of Intervals

    CF1034D Intervals of Intervals

    题目大意

    题目链接

    给定数轴上的 (n) 条线段 ([a_i, b_i])(1leq ileq n)(1leq a_i < b_ileq 10^9))。

    定义区间 ([l, r])(1leq lleq rleq n))的价值为 (l,dots ,r) 这些线段的并的长度。

    请你选择 (k) 个不同的区间,最大化所选区间的价值之和,并输出这个最大值。

    数据范围:(1leq nleq 3 imes 10^5)(1leq kleq min{frac{n(n + 1)}{2},10^9})

    本题题解

    考虑一个子问题:有 (n) 条线段,(q) 次询问,每次问一个区间 ([l, r])(1leq lleq rleq n))里的线段的并的长度。

    可以离线,按编号从 (1)(n) 考虑每条线段,假设当前线段 (i) 是询问的右端点,我们维护每个左端点的答案。

    与此同时,对数轴上所有位置 (p),维护最后一个覆盖到它的线段编号 (mathrm{lst}(p)),特别地,如果位置 (p) 没有被覆盖过,认为 (mathrm{lst}(p) = 0)。那么一次询问 ([l,i]) 的答案就是 (mathrm{lst}) 值大于等于 (l) 的位置有多少个。

    加入线段 (i),相当于把 ([a_i, b_i]) 这段区间里所有位置的 (mathrm{lst}) 值改为 (i)。假设一个位置原本的 (mathrm{lst}) 值为 (i'),那么 ([i' + 1, i]) 里的左端点,答案都要增加 (1)

    把相邻、且 (mathrm{lst}) 值相同的位置,缩成一条线段。那么只需要用 ( exttt{std::set}) 维护若干条线段。每个 (i) 只会带来新增 (mathcal{O}(1)) 条线段(虽然可能它会一次删去很多线段,但这个复杂度是均摊的,所以暴力删就行)。删除一个原有线段,对答案数组(每个左端点的答案)带来的修改是区间加,用线段树维护答案数组即可。

    总时间复杂度 (mathcal{O}(nlog n))。另外提一句,把线段树的修改可持久化一下,该问题也可以在线做。


    回到本题,我们显然是选价值最大的 (k) 个区间。考虑二分价值第 (k) 大的区间的价值。设当前二分值为 (mathrm{mid}),问题转化为求:价值大于等于 (mathrm{mid}) 的区间数量,根据数量 (<)(geq k),调整二分的上下界。

    设区间 ([l, r]) 的价值为 (v(l, r))。那么显然有:(v(l, r)geq v(l + 1, r))(v(l, r)geq v(l, r - 1)) ((l < r))。

    我们仍然从 (1)(n) 枚举右端点。对于同一个右端点 (i),设每个左端点 (l) 的答案为 (f_i(l)),即 (f_i(l) = v(l, i))。那么有:(f_i(1)geq f_i(2)geq dotsgeq f_i(i))

    记最后一个满足 (f_i(l)geq mathrm{mid})(l)(L(i))。特别地,如果 (f_i(1) < mathrm{mid})(L_i(i) = 0)。我们要求的就是 (sum_{i = 1}^{n}L(i))。如果用线段树维护 (f) 数组(支持区间加),那么 (L(i)) 可以在线段树上二分求出来,不过这样总时间复杂度是 (mathcal{O}(nlog^2 n)) 的。还可以继续优化。

    考虑到 (L(1)leq L(2)leq dotsleq L(n)),所以可以在枚举 (i) 的同时,用 ( ext{two pointers}) 的方法维护 (L(i))。在二分之前,先预处理出要做的区间加法(也就是把 ( exttt{set})(log) 放在二分外面),然后用差分来实现区间加。

    时间复杂度 (mathcal{O}(nlog n))

    有一个要注意的细节是,二分出第 (k) 大的价值 (v) 后,答案并不一定是所有 (geq v) 的价值之和。因为价值等于 (v) 的区间可能有多个,超过 (k) 个的部分我们不能取。

    参考代码

    实际提交时建议使用读入、输出优化,详见本博客公告。

    // problem: CF1034D
    #include <bits/stdc++.h>
    using namespace std;
    
    #define mk make_pair
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    
    template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
    template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }
    
    const int MAXN = 3e5;
    const int MAXR = 1e9;
    
    int n, K;
    pii seg[MAXN + 5];
    
    vector<pii> ev[MAXN + 5];
    
    #define mk3(x, y, z) mk(mk(x, y), z)
    #define IT set<pair<pii, int> > :: iterator
    
    set<pair<pii, int> > s;
    
    void insert(int sl, int sr, int sid) {
    	IT it = s.lower_bound(mk3(sl, 0, 0));
    	if (it != s.begin()) {
    		--it;
    		if (it -> fi.se > sl) {
    			pair<pii, int> tmp = *it;
    			s.erase(it);
    			s.insert(mk3(tmp.fi.fi, sl, tmp.se));
    			s.insert(mk3(sl, tmp.fi.se, tmp.se));
    		}
    	}
    	
    	it = s.lower_bound(mk3(sr, 0, 0));
    	if (it != s.begin()) {
    		--it;
    		if (it -> fi.se > sr) {
    			pair<pii, int> tmp = *it;
    			s.erase(it);
    			s.insert(mk3(tmp.fi.fi, sr, tmp.se));
    			s.insert(mk3(sr, tmp.fi.se, tmp.se));
    		}
    	}
    	
    	IT bg = s.lower_bound(mk3(sl, 0, 0));
    	IT ed = bg;
    	
    	if (bg != s.end() && bg -> fi.se <= sr) {
    		int lst = sl;
    		do {
    			int cl = ed -> fi.fi;
    			int cr = ed -> fi.se;
    			
    			if (cl != lst)
    				ev[sid].push_back(mk(1, cl - lst));
    			ev[sid].push_back(mk(ed -> se + 1, cr - cl));
    			
    			lst = cr;
    			
    			++ed;
    		} while(ed != s.end() && ed -> fi.se <= sr);
    		
    		if (lst != sr)
    			ev[sid].push_back(mk(1, sr - lst));
    		
    		s.erase(bg, ed);
    	} else {
    		ev[sid].push_back(mk(1, sr - sl));
    	}
    	
    	s.insert(mk3(sl, sr, sid));
    	
    //	cerr << "**********" << endl;
    //	for (it = s.begin(); it != s.end(); ++it) {
    //		cerr << (it -> fi.fi) << " " << (it -> fi.se) << endl;
    //	}
    }
    #undef IT
    #undef mk3
    
    int f[MAXN + 5];
    ll calc(int min_size) {
    	// 并的大小 >= min_size 的区间有几个
    	// 时间复杂度 O(n)
    	
    	for (int i = 1; i <= n; ++i) {
    		f[i] = 0; // 清空
    	}
    	
    	int l = 0;
    	ll res = 0;
    	for (int i = 1; i <= n; ++i) {
    		// 右端点: i
    		
    		for (int j = 0; j < SZ(ev[i]); ++j) {
    			
    			int p = ev[i][j].fi;
    			int v = ev[i][j].se;
    			
    			ckmax(p, l);
    			
    			// 对 l = p...i 的答案, 加上 v
    			
    			f[p] += v;
    			f[i + 1] -= v;
    			
    		}
    		
    		// l: 使得 [l, i] 的答案 >= min_size 的最大的 l
    		while (l + 1 <= i && f[l + 1] + f[l] >= min_size) {
    			f[l + 1] += f[l];
    			if (l + 1 > 1)
    				assert(f[l + 1] <= f[l]);
    			++l;
    		}
    		
    //		cerr << "** " << l << endl;
    		
    		res += l;
    	}
    	
    	return res;
    }
    
    ll calc_ans(int min_size) {
    	// 并的大小 >= min_size 的区间的答案之和
    	// 时间复杂度 O(n)
    	
    	for (int i = 1; i <= n; ++i) {
    		f[i] = 0; // 清空
    	}
    	
    	int l = 0;
    	ll sum = 0, res = 0;
    	for (int i = 1; i <= n; ++i) {
    		// 右端点: i
    		
    		for (int j = 0; j < SZ(ev[i]); ++j) {
    			
    			int p = ev[i][j].fi;
    			int v = ev[i][j].se; // 对 l = p...i 的答案, 加上 v
    			
    			if (p <= l) {
    				sum += (ll)v * (l - p + 1);
    			}
    			ckmax(p, l);
    			
    			f[p] += v;
    			f[i + 1] -= v;
    		}
    		
    		// l: 使得 [l, i] 的答案 >= min_size 的最大的 l
    		while (l + 1 <= i && f[l + 1] + f[l] >= min_size) {
    			f[l + 1] += f[l];
    			sum += f[l + 1];
    			++l;
    		}
    		res += sum;
    	}
    	
    	return res;
    }
    
    int main() {
    	cin >> n >> K;
    	
    	for (int i = 1; i <= n; ++i) {
    		cin >> seg[i].fi >> seg[i].se;
    	}
    	
    	for (int i = 1; i <= n; ++i) {
    		insert(seg[i].fi, seg[i].se, i);
    	}
    	
    	int l = 0, r = MAXR;
    	while (l < r) {
    		int mid = (l + r + 1) >> 1;
    		if (calc(mid) >= K) {
    			l = mid;
    		} else {
    			r = mid - 1;
    		}
    	}
    	
    //	cerr << l << endl;
    	cout << calc_ans(l + 1) + (ll)l * (K - calc(l + 1)) << endl;
    	
    	return 0;
    }
    
  • 相关阅读:
    (四)STL中的算法
    (三)openssl库实现对称和非对称加密
    (十一)etcd项目
    (十二)插件之dlopen/dlsym/dlclose 加载动态链接库
    (十一)访问权限关键字publi/private/protected
    RESTful架构
    (零)TCP/IP详解综述
    (二)辗转相除法求最大公约数
    (一)简单的TcpServer
    SpringMVC异常处理
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/14403936.html
Copyright © 2011-2022 走看看