題意:給定n條線段的左右端點,求兩條有公共點的線段的並的長度減去它們的交的長度最大(\(n<=2e5\) \(1<=L,R<=1e9\))
分析:不妨設\(L_i<=L_j<=R_i\),線段異或和為\(ans\),兩條線段的右端點有兩種情況:
1.\(R_j>=R_i\)
此時 \(ans=R_j-R_i+L_j-L_i=L_j+R_j-(L_i+R_i)\)
2.\(R_j<=R_i\)
此時 $ ans=R_i-L_i-(R_j-L_j) $
按照線段的左端點從小到大排序(所以這是一個二維數點問題),對左端點相同的,按照右端點從大到小排序,對於第i條線段,每次查詢\([L_i,R_i]\)內\(L+R\)的最小值(case1)和 \([R_i,n]\) 內\(R-L\)的最大值(case2),更新答案,然後在當前線段的右端點維護\(L+R\)的最小值和\(R-L\)的最大值。這個查詢和修改操作可以用線段樹來維護。由於端點座標可能很大,可以預先離散化後再處理。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int n,cnt;
struct line{int l,r;}d[N],g[N];
struct segtree{int l,r,len,net;}s[N<<3];
bool cmp(line c,line d){if(c.l==d.l)return c.r>d.r;return c.l<d.l;}
int h[N<<1],num;
void build(int i,int l,int r)
{
s[i].l=l;s[i].r=r;
if(l==r)
{
s[i].len=h[g[l].r]-h[g[l].l]+1;
s[i].net=g[l].r;
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);build(i<<1|1,mid+1,r);
s[i].net=max(s[i<<1].net,s[i<<1|1].net);
s[i].len=max(s[i<<1].len,s[i<<1|1].len);
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&d[i].l,&d[i].r);
h[i]=d[i].l;h[i+n]=d[i].r;
}
sort(h+1,h+1+n*2);
num=unique(h+1,h+1+n*2)-h-1;
for(int i=1;i<=n;++i)
{
d[i].l=lower_bound(h+1,h+1+num,d[i].l)-h;
d[i].r=lower_bound(h+1,h+1+num,d[i].r)-h;
}
sort(d+1,d+1+n,cmp);
for(int i=1;i<=n;++i)
{
if(d[i].l!=d[i-1].l)
{
++cnt;
g[cnt].l=d[i].l;
g[cnt].r=d[i].r;
}
}
build(1,1,num);
}
int quelen(int i,int x,int y)
{
if(s[i].l>=x && s[i].r<=y)return s[i].len;
int mid=(s[i].l+s[i].r)>>1,sum=0;
if(x<=mid)sum=max(sum,quelen(i<<1,x,y));
if(y>mid)sum=max(sum,quelen(i<<1|1,x,y));
return sum;
}
int quemx(int i,int x,int y)
{
if(s[i].l>=x && s[i].r<=y)return s[i].net;
int mid=(s[i].l+s[i].r)>>1,sum=0;
if(x<=mid)sum=max(sum,quemx(i<<1,x,y));
if(y>mid)sum=max(sum,quemx(i<<1|1,x,y));
return sum;
}
void work()
{
int ans=0;
for(int i=1;i<=cnt;++i)
{
if(g[i].r<num)
ans=max(ans,h[g[i].r]-h[g[i].l]+1+quelen(1,g[i].r+1,num));
ans=h[quemx(1,g[i].l,g[i].r)]-h[g[i].l]+1;
}
cout<<ans;
}
int main()
{
init();
work();
return 0;
}