2018.10.29【BZOJ4979】[Lydsy1708月赛]凌晨三点的宿舍(分治)(树状数组)

传送门


讲个故事:

这是一个悲伤的故事。

我一个人,由于做过一道弱化版的题目,看出了正解,从昨天半夜11:30开始肝这道题,一顿乱搞之后过了样例,然后一直 R E RE RE,期间重构了一次。

这个故事告诉我们注意负数下标是多么的重要。

然后今天早上重构了三次,一直WA。。。

最后学习了 D Z Y O DZYO DZYO的写法,结果一发就 A A A了????

这个故事告诉我们,选择一个编程复杂度不高的写法是多么的重要。

感谢 D Z Y O DZYO DZYO在我绝望无助的时候对我的无私帮助%%%%%%%%%%%%%%%%%。

思路:

一看 O ( n 2 ) O(n^2) O(n2)暴力其实是很好打的,枚举每一个宿舍 O ( n ) O(n) O(n),先把和它同一列在它下面的处理完,然后 O ( n ) O(n) O(n)向右边扫一遍,更新遇到的最低的房顶 m i n n minn minn,两个房间 i , j i,j i,j之间的距离就是 h i + h j + j − i − 2 ∗ m i n n h_i+h_j+j-i-2*minn hi+hj+ji2minn

那么考虑这个求出的 m i n n minn minn能否帮助我们处理更多信息。

显然我们如果选择了某一个点为中心,处理出所有其他点到它路径上的 m i n n minn minn。那么每一个它左边的点到它右边的点的路径上的 m i n n minn minn都能够知道。就是两者取小就行了。

那么考虑分治,每次二分选择分治中心(实际上,我们的分治中心不是某一个点,而是某两栋建筑的分界线)。

计算出每个点到分治中心的 m i n n minn minn,那么 i i i能够达到 j j j的条件就是 h i + h j + j − i − 2 ∗ m i n { m i n n i , m i n n j } ≤ k h_i+h_j+j-i-2*min\{minn_i,minn_j\}\leq k hi+hj+ji2min{minni,minnj}k,我们只讨论当 m i n = m i n n i min=minn_i min=minni时的情况,另外的情况可以类似推导出来。

那么原式可化为 h i − i − 2 ∗ m i n n i ≤ k − h j − j h_i-i-2*minn_i\leq k-h_j-j hii2minnikhjj
好的这道题差不多就要完了。

上面的式子告诉我们,每一个 j j j在当前分治中能够到达多少个 m i n n minn minn较小的 i i i,就是查询有多少个 i i i满足 h i − i − 2 ∗ m i n n i ≤ k − h j − j h_i-i-2*minn_i\leq k-h_j-j hii2minnikhjj
即查询前缀个数和。

我们维护一左一右两个树状数组,支持单点加,查询前缀和就行了,为保证当前询问出来的所有 i i i m i n n minn minn都比 j j j小,我们需要先对所有节点按照 m i n n minn minn排个序。

然后就是实现上的一些细节,这个请读者自己照着代码体会一下吧。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int B=200005;
cs int N=B*5,L=B*3,R=B*5;

#define lowbit(x) (x&(-x))
struct BIT{
	int val[R];
	inline void add(int pos,int v){
		pos+=L;
		for(;pos<R;pos+=lowbit(pos))val[pos]+=v;
	}
	inline int query(int pos,int res=0){
		pos=min(pos+L,R-1);
		for(;pos;pos-=lowbit(pos))res+=val[pos];
		return res;
	}
}b0,b1;

int n,m,k,h[B];
struct node{
	int x,y,val;
	node(cs int &_x=0,cs int &_y=0,cs int &_val=0):x(_x),y(_y),val(_val){}
	friend bool operator<(cs node &a,cs node &b){
		return a.val<b.val;
	}
};

ll ans;

inline void work(vector<pair<int,int> > &q){
	for(int re i=0,j=0;i<q.size();++i){
		while(q[j].second+k<q[i].second)++j;
		ans+=i-j;
	}
}

inline void solve(int l,int r,vector<pair<int,int> > &q){
	if(q.empty())return ;
	if(l==r)return work(q);
	int mid=(l+r)>>1;
	vector<pair<int,int> > ql,qr;
	for(int re i=0;i<q.size();++i)
	(q[i].first<=mid?ql:qr).push_back(q[i]);
	
	solve(l,mid,ql);
	solve(mid+1,r,qr);
	
	vector<node> data;
	for(int re i=ql.size()-1,now=mid,minn=h[now];~i;--now){
		minn=min(minn,h[now]);
		while(~i&&ql[i].first==now){
			data.push_back(node(now,ql[i].second,min(minn,ql[i].second)));
			--i;
		}
	}
	
	for(int re i=0,now=mid+1,minn=h[now];i<qr.size();++now){
		minn=min(minn,h[now]);
		while(i<qr.size()&&qr[i].first==now){
			data.push_back(node(now,qr[i].second,min(minn,qr[i].second)));
			++i;
		}
	}
	
	sort(data.begin(),data.end());
	
	for(int re i=0;i<data.size();++i){
		node &t=data[i];
		if(t.x<=mid){
			b0.add(t.y-t.x-2*t.val,1);
			ans+=b1.query(k+t.x-t.y);
		}
		else{
			b1.add(t.y+t.x-2*t.val,1);
			ans+=b0.query(k-t.x-t.y);
		}
	}
	
	for(int re i=0;i<data.size();++i){
		node &t=data[i];
		if(t.x<=mid)b0.add(t.y-t.x-2*t.val,-1);
		else b1.add(t.y+t.x-2*t.val,-1);
	}
	
}

vector<pair<int,int> > q;
signed main(){
	n=getint();
	k=getint();
	for(int re i=1;i<=n;++i)h[i]=getint();
	m=getint();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint();
		q.push_back(make_pair(u,v));
	}
	sort(q.begin(),q.end());
	solve(1,n,q);
	cout<<ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值