链接:https://www.nowcoder.com/acm/contest/80/E
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
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
分析: 两条线段有公共点,分为两种情况:
1.线段a内部包含线段b 贡献为: (a.y-a.x)-(b.y-b.x)
2.部分覆盖的线段a和线段b, 贡献为 a.y+a.x-b.y-b.x;
所以我们需要分别维护x+y和x-y的值,题目需要求最大的贡献,
那么对于一条线段a,我们需要找到所有与它有公共点的线段中,贡献最大的。
解法一:优先队列
用两个优先队列分别维护x+y和x-y的最值
将所有线段分为 入点和出点,并将入点标记为-i,出点标记为i,并将这些点用以升序进行排列
然后将这些点,从左到右进行扫描,比如当遇到了某个入点的时候,首先考虑优先队列中能与它产生最大贡献的线段,
也就是队头的线段是否和它有公共点。如果在枚举的过程中,已经遇到过了该线段的出点,则一定没有公共点,并让该线段出队
,继续判断下一条线段。反之,则更新最优解。 完成上述操作后,在把枚举到的这条线段放入到优先队列中。
如果在枚举的过程中遇到的是出点,则只需要进行标记,供上述判定时使用。
代码如下(时间复杂度nlogn)
#include <cstdio> #include <algorithm> #include <iostream> #include <queue> using namespace std; const int MAXN=4e5+10; int t; int vis[MAXN]; struct node { int x; int y; node(int a=0,int b=0):x(a),y(b){}; }poi[MAXN],q[MAXN]; bool operator <(node a,node b) { if(a.x!=b.x) return a.x<b.x; return a.y<b.y; } priority_queue<node>Q1; priority_queue<node>Q2; int main() { int a,b,cnt,ans; ans=0; ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>t; cnt=1; for(int k=1;k<=t;k++) { cin>>a>>b; q[k].x=a; q[k].y=b; poi[cnt].x=a,poi[cnt].y=-k; //对于入点和出点的处理 cnt++; poi[cnt].x=b,poi[cnt].y=k; cnt++; } sort(poi+1,poi+cnt); for(int i=1;i<cnt;i++) { if(poi[i].y>0) vis[poi[i].y]=1; //标记出点 else { int k=-poi[i].y; while(!Q1.empty()&&vis[Q1.top().y])Q1.pop(); //已经遇到了的出点,则无公共点,出队 while(!Q2.empty()&&vis[Q2.top().y])Q2.pop(); if(!Q1.empty()) ans=max(ans,q[k].x+q[k].y+Q1.top().x);//有公共点,更新最优解 if(!Q2.empty()) ans=max(ans,Q2.top().x-q[k].y+q[k].x); Q1.push(node(-q[k].x-q[k].y,k)); Q2.push(node(q[k].y-q[k].x,k)); } } cout<<ans<<endl; return 0; }
解法二:线段树
首先将所有的线段按照x的升序排列,x相同的,按y的升序排列。
然后从依次枚举每一条线段,找到与它有公共点的线段的范围。
因为经过排列后,线段是有序的,所以可以采用二分方法来找到这个范围。
现在需要找到这个范围中 x+y和x-y的最值来更新最优解。
需要维护区间最值,可以采用线段树。
代码如下(时间复杂度(O(nlognlogn))
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int MAXN=2e5+100; typedef long long ll; #define INF 0x3f3f3f3f int maxx1[MAXN<<2]; int maxx2[MAXN<<2]; int res1; int res2; struct Node{ int x; int y; }poi[MAXN]; bool cmp(Node a,Node b) { if(a.x!=b.x) return a.x<b.x; return a.y<b.y; } struct node { int l; int r; }tree[MAXN<<2]; void PushUp(int rt) { maxx1[rt]=max(maxx1[rt<<1],maxx1[rt<<1|1]); maxx2[rt]=max(maxx2[rt<<1],maxx2[rt<<1|1]); } void BuildTree(int l,int r,int rt) { tree[rt].l=l; tree[rt].r=r; if(l==r) { maxx1[rt]=poi[l].x+poi[l].y; maxx2[rt]=poi[l].x-poi[l].y; return; } int mid=(tree[rt].l+tree[rt].r)/2; BuildTree(l,mid,rt<<1); BuildTree(mid+1,r,rt<<1|1); PushUp(rt); } void Query(int l,int r,int rt) { if(tree[rt].l==l&&tree[rt].r==r) { res1=max(res1,maxx1[rt]); res2=max(res2,maxx2[rt]); return; } int mid=(tree[rt].l+tree[rt].r)/2; if(r<=mid)Query(l,r,rt<<1); else if(l>mid)Query(l,r,rt<<1|1); else { Query(l,mid,rt<<1); Query(mid+1,r,rt<<1|1); } PushUp(rt); } int main() { int n,ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&poi[i].x,&poi[i].y); sort(poi+1,poi+n+1,cmp); BuildTree(1,n,1); for(int i=1;i<=n;i++) { int ll=i; int rr=n+1; while(ll+1<rr) { int mid=(ll+rr)/2; if(poi[mid].x<=poi[i].y) ll=mid; else rr=mid; } if(ll==i)continue; res1=-INF; res2=-INF; Query(i+1,ll,1); ans=max(ans,res1-poi[i].x-poi[i].y); ans=max(ans,poi[i].y-poi[i].x+res2); } printf("%d ",ans); return 0; }
解法三:RMQ
将维护区间最值的方法换成RMQ即可
代码如下(时间复杂度(O(nlognlogn))
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> using namespace std; const int MAXN=2e5+100; typedef long long ll; #define INF 0x3f3f3f3f int maxsum[MAXN][20]; int minsum[MAXN][20]; int maxx; int minn; struct Node{ int x; int y; }poi[MAXN]; bool cmp(Node a,Node b) { if(a.x!=b.x) return a.x<b.x; return a.y<b.y; } void RMQ_In(int num) //预处理->O(nlogn) { for(int j = 1; j < 20; ++j) for(int i = 1; i <= num; ++i) if(i + (1 << j) - 1 <= num) { maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]); minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]); } } void RMQ_Query(int src,int des) { if(src>des) swap(src,des); int k=(int)(log(des-src+1.0)/log(2.0)); maxx=max(maxsum[src][k],maxsum[des-(1<<k)+1][k]); minn=min(minsum[src][k],minsum[des-(1<<k)+1][k]); } int main() { int n,ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&poi[i].x,&poi[i].y); sort(poi+1,poi+n+1,cmp); for(int i=1;i<=n;i++) { minsum[i][0]=poi[i].y-poi[i].x; maxsum[i][0]=poi[i].y+poi[i].x; } RMQ_In(n); for(int i=1;i<=n;i++) { int ll=i; int rr=n+1; while(ll+1<rr) { int mid=(ll+rr)/2; if(poi[mid].x<=poi[i].y) ll=mid; else rr=mid; } if(ll==i)continue; maxx=-INF; minn=INF; RMQ_Query(i+1,ll); ans=max(ans,maxx-poi[i].x-poi[i].y); ans=max(ans,poi[i].y-poi[i].x-minn); } printf("%d ",ans); return 0; }