LGP10281 [USACO-G 24OPEN] Grass Segment 学习笔记

LGP10281 [USACO-G 24OPEN] Grass Segment 学习笔记

Luogu Link

题意简述

给定数轴正半轴上的 \(n\) 个区间 \([l_i,r_i]\)。对于每个区间 \(i\),计算和它重合了至少 \(k_i\) 长度的区间 \(j\) 的数量(\(j\neq i,k_i\le r_i-l_i\))。

做法解析

如果 \(j\) 是满足 \(i\) 条件的区间,则有:\(k_i\le \min(r_i,r_j)-\max(l_i,l_j)\)。这一个限制可以拆成四个限制:

\[k_i\le r_i-l_i\to 无需考虑 \]

\[k_i\le r_j-l_j\to r_j-l_j\ge k_i \]

\[k_i\le r_i-l_j\to l_j\le r_i-k_i \]

\[k_i\le r_j-l_i\to r_j\ge k_i+l_i \]

我们发现这玩意应该就是三维偏序了。具体怎么个做法呢?

首先我们发现其中有两维的关系都形如 \(w_j\ge w_i\),有一维形如 \(w_j\le w_i\)。我们把后者作为第三维放树状数组上,这样的话树状数组询问的是一个前缀,好看。

然后重点来了:如果你在此之前只写过CDQ分治的模板,那么务必记住:适当情况下把询问和提供贡献者分开!让一部分anob承担询问的职责,一部分anob承担提供贡献的职责,这样写起来更加简单明了!

最后,我们要把 \(ans_i\)\(1\)。因为每个区间都会把自己算进答案一次。

代码实现

#include <bits/stdc++.h>
using namespace std;
namespace obasic{
    template <typename _T>
    void readi(_T &x){
        _T k=1;x=0;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
        x*=k;return;
    }
    template <typename _T>
    void writi(_T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)writi(x/10);
        putchar(x%10+'0');
    }
    template <typename _T>
    int lwberi(_T carr[],int alen,_T val){return lower_bound(carr+1,carr+alen+1,val)-carr;}
};
using namespace obasic;
const int MaxN=2e5+5;
int N,L,R,K,nlb,B[MaxN<<1],ans[MaxN];
struct anob{int x,y,z,id;}A[MaxN<<1];
bool cmpx(anob a,anob b){return a.x!=b.x?a.x>b.x:(a.y!=b.y?a.y>b.y:a.z<b.z);}
bool cmpy(anob a,anob b){return a.y!=b.y?a.y>b.y:(a.x!=b.x?a.x>b.x:a.z<b.z);}
struct BinidTree{
    int n,t[MaxN<<1];
    void init(int x){n=x,fill(t,t+n+1,0);}
    int lowbit(int x){return x&(-x);}
    void add(int p,int x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
    int gts(int p){int res=0;for(;p;res+=t[p],p-=lowbit(p));return res;}
    int getsum(int l,int r){return gts(r)-gts(l-1);}
}BiT;
void cdqdac(int cl,int cr){
    if(cl==cr)return;int cmid=(cl+cr)>>1;
    cdqdac(cl,cmid),cdqdac(cmid+1,cr);
    sort(A+cl,A+cmid+1,cmpy),sort(A+cmid+1,A+cr+1,cmpy);
    int p=cl,q=cmid+1;for(;q<=cr;q++){
        for(;p<=cmid&&A[p].y>=A[q].y;p++){
            if(!A[p].id)BiT.add(A[p].z,1);
        }
        if(A[q].id)ans[A[q].id]+=BiT.gts(A[q].z);
    }
    for(int i=cl;i<p;i++)if(!A[i].id)BiT.add(A[i].z,-1);
}
int main(){
    readi(N);
    for(int i=1;i<=N;i++){
        readi(L),readi(R),readi(K);
        A[i]={R-L,R,L,0};
        A[i+N]={K,K+L,R-K,i};
        B[i]=L,B[i+N]=R-K;
    }
    sort(B+1,B+(N<<1)+1);nlb=unique(B+1,B+(N<<1)+1)-(B+1);
    for(int i=1;i<=(N<<1);i++)A[i].z=lwberi(B,nlb,A[i].z);
    sort(A+1,A+(N<<1)+1,cmpx);BiT.init(nlb);cdqdac(1,N<<1);
    for(int i=1;i<=N;i++)writi(ans[i]-1),puts("");
    return 0;
}

反思总结

两类anob分开写,简单明了不易错!
推荐这道题作为你的第二道CDQ分治!

posted @ 2025-02-21 09:23  矞龙OrinLoong  阅读(7)  评论(0编辑  收藏  举报