LGP10281 [USACO-G 24OPEN] Grass Segment 学习笔记
LGP10281 [USACO-G 24OPEN] Grass Segment 学习笔记
题意简述
给定数轴正半轴上的 \(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分治!