【洛谷P7290】「EZEC-5」暴力出奇迹

分块。

题目大意:

给定一个平面,有 $n$ 条线段,第 $i$ 条线段的端点为 $(i,a_i)$ 和 $(i,b_i)$。

$m$ 组询问,每次给出 $l,r,x,y$,你要选择一个 $i\in[l,r]$ 使得以 $(x,i)$ 和 $(y,i)$ 为端点的线段与平面上已有线段的交点个数最多。需要回答最多的交点个数。

题解:

这篇题解仅讲述问题的转化。

先将题意转述为更容易理解的形式。

有 $n$ 个操作,第 $i$ 个操作为 $[l_i,r_i]$,表示对 $[l_i,r_i]$ 区间加一。

$m$ 组询问,每次给出 $l,r,x,y$,要求对一个初始为空的序列执行编号在 $x$ 到 $y$ 之间的操作,然后查询区间 $[l,r]$ 的最大值。

考虑对区间加进行差分,即将 $[l,r]$ 的区间加变成 $l$ 处加一,$r+1$ 处减一,然后区间查询,相当于查询右端点在 $[x,y]$ 内的前缀的最大前缀和。不难发现,$1\sim x-1$ 处的值一定会被统计到答案中,因此可以把询问分成查询 $[1,x-1]$ 的和,以及 $[x,y]$ 的区间最大前缀和。前者可以使用二维数点在 $O(n\log n+m\log n)$ 内解决,重点在于求出后者。

对每个 $i$,从 $[l_i,r_i]$ 中可以得到两个三元组 $(l_i,i,1)$,$(r_i,i,-1)$。

我们将所有三元组以第一个元素为关键字从小到大排序,得到一个长度为 $2n$ 的三元组序列。我们称位置 $i$ 的第三个元素为位置 $i$ 的值(为保证结果正确,在第一个元素相同时,值为 $1$ 的在前)。然后一次查询的 $[x,y]$ 对应这个序列上的一个区间。

于是现在的查询操作实际上是:仅保留第二个元素在 $[l,r]$ 内的位置的值,其他位置的值为 $0$ 时,查询 $[x,y]$ 对应的区间的区间最大前缀和。

这个问题有一个强化版的问题,查询的是最大子段和信息,而维护该信息的同时需要维护本题所需的最大前缀和信息。因此直接使用该题的做法即可解决本题。具体可以见我的题解

该做法的时间复杂度为 $O((n+m)\sqrt n)$,空间复杂度为 $O(n+m)$,实现时需要注意常数因子。

Code:

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef unsigned long long u64;
const int N=5e5+5,siz=800,B=siz+2;
int n,m,b[B],tmp[N],prex[N];
#define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,5000000,stdin),pa==pb)?EOF:*pa++
static char buf[5000000],*pa(buf),*pb(buf);
inline int readint() {
    int x=0;char c=gc;
    while(c<'0'||c>'9')c=gc;
    for(;c>='0'&&c<='9';c=gc)x=x*10+(c&15);
    return x;
}
struct ostream {
    static const int size = 1 << 22;
    char buf[size], *vout;
    unsigned map[10000];
    inline ostream() {
        for(int i = 0;i < 10000;++i) {
            map[i] = i % 10 + 48 << 24 | i / 10 % 10 + 48 << 16 | i / 100 % 10 + 48 << 8 | i / 1000 + 48;
        }
        vout = buf + size;
    }
    inline ~ ostream()
    { fwrite(vout,1,buf + size - vout,stdout), std::cerr << double(clock()) / CLOCKS_PER_SEC << '\n'; }
    inline ostream& operator << (unsigned x) {
        for(;x >= 10000;x /= 10000) *--(unsigned*&)vout = map[x % 10000];
        do *--vout = x % 10 + 48; while(x /= 10);
        return * this;
    }
    inline ostream& operator << (char x) {
        *--vout = x;
        return * this;
    }
} Cout;
struct dat{
    int x,v,k;
    inline bool operator<(const dat&rhs)const{return x==rhs.x?v>rhs.v:x<rhs.x;}
}a[N],g[B],h[B];
inline bool operator < (const dat & rhs, int x) { return rhs.x < x; }
inline bool operator < (int x, const dat & rhs) { return x < rhs.x; }
struct xppsb { int V, K; } AAAA[N];
struct info{
    int lmx,sum;
    inline info(int a,int b):lmx(a),sum(b){}
    inline info(){}
    inline void set(const info & lhs, const info & rhs) {
        lmx = max(lhs.lmx, lhs.sum + rhs.lmx);
        sum = lhs.sum + rhs.sum;
    }
    inline info operator+(const info&rhs)const{
        return info(max(lmx,sum+rhs.lmx),sum+rhs.sum);
    }
    inline void operator+=(const info&rhs){
        lmx=max(lmx,sum+rhs.lmx),sum+=rhs.sum;
    }
}ans[N],Lt[2000000],Rt[2000000],mem[2000000],*const qt=mem+1;
struct Sinfo{
    int zt;
    info v;
}D[8000000],*ed;
struct que{
    int L,R,l,r;
}q[N];
pair<int,int>Q[N];
void solve(int l,int r,Sinfo*V,int&tot){
    if(l==r){
        V[tot++]=(Sinfo){b[l]<<10|b[r],info(h[l].v,h[r].v)};
        return;
    }
    const int mid=(l+r)/2;
    Sinfo*L=ed;ed+=(mid-l+1)*(mid-l+2);
    Sinfo*R=ed;ed+=(r-mid)*(r-mid+1);
    int cntL=0,cntR=0;
    solve(l,mid,L,cntL);
    solve(mid+1,r,R,cntR);
    int i=l,j=mid+1,t=l,k=0;
    while(i<=mid&&j<=r)
    if(b[i]<b[j])tmp[t++]=b[i++];else tmp[t++]=b[j++];
    while(i<=mid)tmp[t++]=b[i++];
    while(j<=r)tmp[t++]=b[j++];
    for(int i=0;i<cntL;++i)Lt[L[i].zt+1]=L[i].v;
    for(int i=0;i<cntR;++i)Rt[R[i].zt+1]=R[i].v;
    static int LfL[B],LfR[B],RgL[B],RgR[B];
    t=l,k=mid+1;
    for(int i=l;i<=r;++i){
        while(t<=mid&&b[t]<tmp[i])++t;
        while(k<=r&&b[k]<tmp[i])++k;
        LfL[i]=t<=mid?b[t]:B,RgL[i]=k<=r?b[k]:B;
    }
    t=mid,k=r;
    for(int i=r;i>=l;--i){
        while(t>=l&&b[t]>tmp[i])--t;
        while(k>mid&&b[k]>tmp[i])--k;
        LfR[i]=t>=l?b[t]:-1,RgR[i]=k>mid?b[k]:-1;
    }
    for(int i=l;i<=r;++i)b[i]=tmp[i];
    for(int i=l;i<=r;++i) {
        int idx = tot - i;
        for(int j=i;j<=r;++j){
            const info&ztL=Lt[LfL[i]<<10|(LfR[j]+1u)],&ztR=Rt[RgL[i]<<10|(RgR[j]+1u)];
            const int nt=b[i]<<10|b[j];
            V[idx + j].zt = nt;
            V[idx + j].v.set(ztL, ztR);
        }
        tot += r - i + 1;
    }
    ed=L;
}
inline void up(int & x, int y) {
    if(x < y) x = y;
}
info get(int L,int R,int l,int r){
    int rx=0,sm=0;
    const unsigned A = r - l;
    for(int i=L;i<=R;++i)if(AAAA[i].K-l<=A)up(rx,sm+=AAAA[i].V);
    return info(rx,sm);    
}
int BT[N];
inline void add(int i,int x){for(;i<=n>>1;i+=i&-i)BT[i]+=x;}
inline int ask(int i){int x=0;for(;i;i&=i-1)x+=BT[i];return x;}
void _2D(){
    sort(Q+1,Q+m+1);
    for(int i=1,it=1;i<=m;++i){
        while(it<=Q[i].fi)add(a[it].k,a[it].v),++it;
        int id=Q[i].se;
        prex[id]=ask(q[id].r)-ask(q[id].l-1);
    }
}
int main(){
    n=readint()<<1;
    for(int i=1;i<=n;i+=2){
        int x=readint(),y=readint();
        a[i]=(dat){x,1,(i+1)/2},a[i+1]=(dat){y,-1,(i+1)/2};
    }
    sort(a+1,a+n+1);
    for(int i = 1;i <= n;++i) {
        AAAA[i].V=a[i].v;
        AAAA[i].K=a[i].k;
    }
    m=readint();
    for(int i=1;i<=m;++i){
        int x=readint(),y=readint();
        q[i].l=readint(),q[i].r=readint();
        q[i].L=lower_bound(a+1,a+n+1,x)-a;
        q[i].R=upper_bound(a+1,a+n+1,y)-a-1;
        Q[i]=make_pair(q[i].L-1,i);
    }
    _2D();
    for(int L=1,R;L<=n;L=R+1){
        R=min(n,L+siz-1);
        int len=R-L+1;
        for(int i=L;i<=R;++i)g[i-L]=h[i-L]=(dat){a[i].k,a[i].v,i-L};
        sort(g,g+len);
        for(int i=0;i<len;++i)b[g[i].k]=i;
        ed=D+len*(len+1);
        int tot=0;
        solve(0,len-1,D,tot);
        for(int i=0;i<tot;++i)qt[D[i].zt+1]=D[i].v;
        static int _[N];
        for(int i=1,it=0;i<=n;++i){
            while(it<len&&g[it].x<i)++it;
            _[i]=it;
        }
        for(int i=1;i<=m;++i)if(q[i].L<L&&R<q[i].R)
        ans[i]+=qt[_[q[i].l]<<10|_[q[i].r+1]];
    }
    for(int i=m;i;--i){
        int l=q[i].L,r=q[i].R;
        int res=0;
        const int bL=(l-1)/siz+1,bR=(r-1)/siz+1;
        if(bR-bL<2)res=get(l,r,q[i].l,q[i].r).lmx;
        else res=(get(l,bL*siz,q[i].l,q[i].r)+ans[i]+get(bR*siz-siz+1,r,q[i].l,q[i].r)).lmx;
        res=prex[i]+max(0,res);
        Cout<<'\n'<<unsigned(res);
    }
    return 0;
}