ut.cpp 最大線段並減線段交 [線段樹]

Glowingfire發表於2024-08-23

題意:給定n條線段的左右端點,求兩條有公共點的線段的並的長度減去它們的交的長度最大(\(n<=2e5\) \(1<=L,R<=1e9\)


分析:不妨設\(L_i<=L_j<=R_i\),線段異或和為\(ans\),兩條線段的右端點有兩種情況:
1.\(R_j>=R_i\)

Rj>=Rj

此時 \(ans=R_j-R_i+L_j-L_i=L_j+R_j-(L_i+R_i)\)
2.\(R_j<=R_i\)

Rj<=Ri

此時 $ 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;
}