题解[CF1045G]

题目链接
提供一个稍微不同的 cdq 分治写法,不依赖 \(K\) 的大小。
首先一个限制为 \(q\) 的值差距不大于 \(K\) 。
于是可以对 q 排完序后对每个下标 i 查 q 在 \(q_i+K\) 内的贡献减去 q 在 \(q_i-K-1\) 内的贡献,
这一维便可直接用 \(cdq\) 在外层解决。
而后是“能相互看到”的限制。
设每个人 \(i\) 能看到的最左、最右及其下标分别为 \(l_i,r_i,pos_i\) 。
则对于一对点 \(i,j\) 能相互看到的充要条件为 \(l_j\leq pos_i \leq pos_j \leq r_i\) 。
那在分治每层只需对 \([l,mid]\) 的以 \(l\) 排序,\([mid+1,r]\) 的以 \(pos\) 排序,
然后在 \(l_j\leq pos_i\) 中查 \(pos_i \leq pos_j \leq r_i\) 的 \(j\) 的个数,用个树状数组即可。
但这还没完,因为每个点对在其 \(pos\) 不相同是只算了一次,但是在 \(pos\) 相同时被算两次。
一种解决方法是再找一遍 \(pos\) 相同的点两两间的贡献减掉,但比较麻烦。
更快的方法是再累加一遍在 \(l_j\leq pos_i\) 中 \(pos_i < pos_j \leq r_i\) 的 \(j\) 的个数。
然后把 \(ans\) 除以 \(2\) ,这相当于对 \(pos\) 不同的点对再算一次,除后就是真正答案。
时间复杂度为 \(O(n\times log(n)^2)\) 。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,_n,nn,x,r,y,tot;
long long ans;int b[N];
struct cdq{
	int l,r,pos,q,opt;
	cdq()=default;
	cdq(int _l,int _r,int _pos,int _q,int _opt):l(_l),r(_r),pos(_pos),q(_q),opt(_opt){}
}q[N];char ch;
inline void read(int &x){
	x=0;ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
void write(long long x){if(x>=10)write(x/10);putchar('0'+x%10);}
bool cmpq(cdq &x,cdq &y){return x.q^y.q?x.q<y.q:abs(x.opt)<abs(y.opt);}
bool cmpl(cdq &x,cdq &y){return x.l<y.l;}
bool cmppos(cdq &x,cdq &y){return x.pos<y.pos;}
int t[N];
#define lowbit(i) i&(-i)
void update(int pos,int v){for(int i=pos;i<=nn;i+=lowbit(i))t[i]+=v;}
void clear(int pos){for(int i=pos;i<=nn;i+=lowbit(i))t[i]=0;}
int inquiry(int pos){int res=0;for(int i=pos;i;i-=lowbit(i))res+=t[i];return res;}
void solve(int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	sort(q+l,q+mid+1,cmpl);sort(q+mid+1,q+r+1,cmppos);
	int j=l;
	for(int i=mid+1;i<=r;i++){
		while(j<=mid&&q[j].l<=q[i].pos){
			if(!q[j].opt)update(q[j].pos,1);
			j++;
		}
		if(q[i].opt)ans+=(2*inquiry(q[i].r)-inquiry(q[i].pos-1)-inquiry(q[i].pos))*q[i].opt;
		//算入pos_i<=pos_j<=r_i以及pos_i<pos_j<=r_i的贡献 
	}
	for(int i=l;i<j;i++)if(!q[i].opt)clear(q[i].pos);
}
main(){
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(x),read(r),read(y);
		b[++_n]=x-r;b[++_n]=x+r;b[++_n]=x;
		q[++tot]=cdq(x-r,x+r,x,y,0);
		q[++tot]=cdq(x-r,x+r,x,y+m,1);
		q[++tot]=cdq(x-r,x+r,x,y-m-1,-1);
		//opt=1代表加上,opt=-1代表减去 
	}
	sort(b+1,b+_n+1);nn=unique(b+1,b+_n+1)-b-1;
	for(int i=1;i<=tot;i++)
		q[i].l=lower_bound(b+1,b+nn+1,q[i].l)-b,
		q[i].r=lower_bound(b+1,b+nn+1,q[i].r)-b,
		q[i].pos=lower_bound(b+1,b+nn+1,q[i].pos)-b;
	sort(q+1,q+tot+1,cmpq);
	solve(1,tot);
	ans-=n;//在统计pos_i<=pos_j<=r_i时会算进自己,应减掉 
	write(ans>>1);
}
上一篇:CDQ分治学习笔记


下一篇:关于偏序问题