链接:https://www.nowcoder.com/acm/contest/80/E
来源:牛客网
题目描述
VVQ 最近迷上了线段这种东西
现在他手上有 n 条线段,他希望在其中找到两条有公共点的线段,使得他们的异或值最大。 定义线段的异或值为它们并的长度减他们交的长度
输入描述:
第一行包括一个正整数 n,表示 VVQ 拥有的线段条数。
接下来 n 行每行包括两个正整数 l,r,表示 VVQ 拥有的线段的 左右端点。
输出描述:
一行一个整数,表示能得到的最大异或值
示例1
输入
3
10 100
1 50
50 100
输出
99
说明
选择第二条和第三条,99-0=99
备注:
1<=n<=200000,1<=l<=r<=1e8
分析:(有点运气成分了)二分预处理+线段树;
先贪心左端排个序(右端同理),遍历每一条线段。对于排序后的每一条线段,左端是升序排列,从右端可以画一条竖线,有交点的线段才会存在异或值。怎么把有交点的线段给找出来呢?由于是有序的,可以根据枚举线段的右端点通过二分所有线段的左端点,且找到的是一个区间【i,res】,在这个满足条件的区间里分两种情况:1,枚举的线段[x1,y1]包含了一部分区间线段[x2, y2],即:x1<=x2<=y2<=y1,对于这种情况:异或值=y1 - x1 - (y2 - x2),只需要维护区间【R-L】的最小值即可;2,枚举的线段[x1,y1]交叉了一部分区间线段[x2, y2],即:x1<=x2<=y1<=y2,对于这种情况:异或值=y2 + x2 - (y1 + x1),只需要维护区间【R+L】的最大值即可。
对于区间可以用线段树维护,这道题主要是二分的预处理,这个解题的关键;
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 10;
int dp1[MAXN << 2], dp2[MAXN << 2], arr[MAXN];
struct node {
int l;
int r;
}s[MAXN];
bool cmp(node x, node y) {
if(x.l != y.l) return x.l < y.l;
return x.r < y.r;
}
inline void pushup1(int root) {
dp1[root] = min(dp1[root << 1], dp1[root << 1 | 1]);
}
inline void pushup2(int root) {
dp2[root] = max(dp2[root << 1], dp2[root << 1 | 1]);
}
inline void build1(int root, int L, int R) {
if(L == R) {
dp1[root] = s[L].r - s[L].l;
return ;
}
int mid = (L + R) >> 1;
build1(root << 1, L, mid);
build1(root << 1 | 1, mid + 1, R);
pushup1(root);
}
inline void build2(int root, int L, int R) {
if(L == R) {
dp2[root] = s[L].r + s[L].l;
return ;
}
int mid = (L + R) >> 1;
build2(root << 1, L, mid);
build2(root << 1 | 1, mid + 1, R);
pushup2(root);
}
inline int query1(int root, int L, int R, int l, int r) {
if(l <= L && R <= r) {
return dp1[root];
}
int ans = 1e9;
int mid = (L + R) >> 1;
if(l <= mid) ans = min(ans, query1(root << 1, L, mid, l, r));
if(r > mid) ans = min(ans, query1(root << 1 | 1, mid + 1, R, l, r));
return ans;
}
inline int query2(int root, int L, int R, int l, int r) {
if(l <= L && R <= r) {
return dp2[root];
}
int ans = 0;
int mid = (L + R) >> 1;
if(l <= mid) ans = max(ans, query2(root << 1, L, mid, l, r));
if(r > mid) ans = max(ans, query2(root << 1 | 1, mid + 1, R, l, r));
return ans;
}
int main() {
int n, l, r;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d %d", &s[i].l, &s[i].r);
sort(s + 1, s + n + 1, cmp);
for(int i = 1; i <= n; ++i) {
arr[i] = s[i].l;
}
build1(1, 1, n);
build2(1, 1, n);
int ans = 0;
for(int i = 1; i <= n; ++i) {
int cnt = s[i].r;
int res = upper_bound(arr + 1, arr + n + 1, cnt) - arr;
ans = max(ans, cnt - s[i].l - query1(1, 1, n, i, res - 1));
ans = max(ans, query2(1, 1, n, i, res - 1) - cnt - s[i].l);
}
printf("%d\n", ans);
return 0;
}