「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)
Posted 逢山开路 遇水架桥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)相关的知识,希望对你有一定的参考价值。
原题传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2687
【题目大意】
给出若干区间,求一个区间的大于等于2的子集,使得 |区间并| 和 |区间交| 的乘积最大。
$1\leq n, L_i, R_i \leq 10^6$
【题解】
把区间去掉包含情况,然后进行排序,变成$L_i$和$R_i$都递增的数列。
然后容易发现取得区间一定是连续的一段。
然后我们推一推决策单调性,我把考试的时候推的写在代码里了
然后直接单调队列优化即可。
然后要注意的是,还有一种情况,也就是包含的情况需要讨论。
我的做法可能比较奇怪,包含的情况,对于每个被包含的区间,贡献最大值是包含它的长度最长的区间。
我们对左端点进行排序,发现要找的包含的一定是右端点大于当前讨论区间的右端点(左端点已经固定小于了),的最长区间。
我们对于区间长度建一棵线段树,然后线段树维护长度在区间内的右端点max,询问相当于在线段树上二分,复杂度$O(logn)$。
总复杂度$O(nlogn)$,竟然没有被卡常23333
代码似乎写的非常丑。。。
# include <queue> # include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long double ld; const int M = 2e5 + 10, N = 1e6 + 10, F = 1e6; const int mod = 1e9 + 7; inline int getint() { int x = 0; char ch = getchar(); while(!isdigit(ch)) ch = getchar(); while(isdigit(ch)) { x = (x<<3) + (x<<1) + ch - ‘0‘; ch = getchar(); } return x; } int n, q[N]; struct pa { int l, r; pa() {} pa(int l, int r) : l(l), r(r) {} friend bool operator < (pa a, pa b) { return a.l < b.l || (a.l == b.l && a.r > b.r); } }p[N], t[N]; int tn = 0; inline bool cmp(pa a, pa b) { return a.l < b.l || (a.l == b.l && a.r < b.r); } /* inline int gs(int x) { int l = 1, r = n, mid; while(1) { if(r-l <= 3) { for (int i=l; i<=r; ++i) if(p[i].r > x) return i; return -1; } mid = l+r>>1; if(p[mid].r > x) r = mid; else l = mid; } return -1; } */ inline ll gsum(int i, int j) { return (ll)(p[i].r-p[j].l) * (ll)(p[j].r-p[i].l); } int RM[N]; struct SMT { # define ls (x<<1) # define rs (x<<1|1) int w[N << 2]; inline void set() { memset(w, 0, sizeof w); } inline void edt(int x, int l, int r, int pos, int d) { if(l == r) { w[x] = max(w[x], d); return ; } int mid = l+r>>1; if(pos <= mid) edt(ls, l, mid, pos, d); else edt(rs, mid+1, r, pos, d); w[x] = max(w[ls], w[rs]); } inline int gs(int x, int l, int r, int R) { if(w[x] < R) return 0; if(l == r) return l; int mid = l+r>>1; if(w[rs] >= R) return gs(rs, mid+1, r, R); else return gs(ls, l, mid, R); } }T; // # include <time.h> int main() { // int tm = clock(); freopen("qyh.in", "r", stdin); freopen("qyh.out", "w", stdout); ll ans = 0, tmp; T.set(); // cin >> n; n = getint(); // for (int i=1; i<=n; ++i) scanf("%d%d", &p[i].l, &p[i].r); for (int i=1; i<=n; ++i) p[i].l = getint(), p[i].r = getint(); sort(p+1, p+n+1); t[tn = 1] = p[1]; T.edt(1, 0, F, p[1].r - p[1].l, p[1].r); int mxr = p[1].r; for (int i=2; i<=n; ++i) { if(p[i].r <= mxr) { tmp = T.gs(1, 0, F, p[i].r); tmp = tmp * (p[i].r - p[i].l); if(tmp > ans) ans = tmp; continue; } t[++tn] = p[i]; T.edt(1, 0, F, p[i].r-p[i].l, p[i].r); mxr = p[i].r; } n = tn; for (int i=1; i<=n; ++i) p[i] = t[i]; // for (int i=1; i<=n; ++i) printf("%d %d\n", p[i].l, p[i].r); int lst = 1, head = 1, tail = 0; for (int i=2; i<=n; ++i) { /* (r_i - l_j) * (r_j - l_i) r_i * r_j + l_i * l_j - l_j * r_j - l_i * r_i max if (r_i - l_j) * (r_j - l_i) > (r_i - l_k) * (r_k - l_i) r_i * r_j + l_i * l_j - l_j * r_j > r_i * r_k + l_i * l_k - l_k * r_k r_i * (r_j - r_k) + l_i * (l_j - l_k) > l_j * r_j - l_k * r_k suppose j>k and j is better than k if i + 1, then r_{i+1} * (r_j - r_k) + l_{i+1} * (l_j - l_k) > l_j * r_j - l_k * r_k suppose j<k and j is better than k if i + 1, then r_{i+1} * (r_j - r_k) + l_{i+1} * (l_j - l_k) > l_j * r_j - l_k * r_k */ /* if(gsum(i, i-1) > gsum(i, lst)) lst = i-1; tmp = gsum(i, lst); // cout << lst << endl; if(tmp > ans) ans = tmp; */ while(head < tail && gsum(i, q[head]) < gsum(i, q[head+1])) ++head; while(head <= tail && gsum(i, i-1) >= gsum(i, q[tail])) --tail; q[++tail] = i-1; if(head <= tail) { // printf("%d %d\n", i, q[head]); tmp = gsum(i, q[head]); if(tmp > ans) ans = tmp; } } cout << ans; // cerr << clock() - tm << " ms" << endl; return 0; }
以上是关于「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)的主要内容,如果未能解决你的问题,请参考以下文章