2018.10.24【校内模拟】小 C 的宿舍(分治)

传送门


解析:

这道题真的就是防AKAK题了,但是对于我这个蒟蒻来说三道题都是防AKAK题,为什么说这道题是防AKAK题呢,主要是为了阻止ldxoildxoiAK的步伐。

蒟蒻考场上打了30pts30pts暴力靠着常数优化勉强卡了60pts60pts,垫底滚粗了。。。

思路:

有点cdqcdq分治的味道,但不是cdqcdq分治。。。

首先ii的房顶到jj的房顶(i<j)(i<j)的距离是hi+hj+ji2×min{hi,hi+1...hj}h_i+h_j+j-i-2\times min\{h_i,h_{i+1}...h_j\}。其实很好理解,我们必须先走到最低的地方绕过去才能过去。

显然我们计算能否从ii的房顶达到jj的房顶可以O(n)O(n),如果预处理区间最小值可以O(1)O(1)。但是要统计所有的话这个就显得有点吃力了。

那么考虑分治,每次选取一个区间<l,r><l,r>,以中间点mid=(l+r)/2mid=(l+r)/2为分界线统计每个lmidl-mid中的点能够到达多少在mid+1rmid+1-r中的点,反过来也是一样的。

然后递归处理lmidl-midmid+1rmid+1-r两个区间,显然这样处理是不重不漏的。

然后就是处理的重头戏了。

定义minni={min{hi,hi+1...hmid}(imid)min{hi,hi1...hmid+1}(imid)minn_i=\begin{cases} min\{h_i,h_{i+1}...h_{mid}\}& (i\leq mid)\\ min\{h_i,h_{i-1}...h_{mid+1}\}& (i\geq mid) \end{cases}

那么iijj的距离就是hi+hj+ji2×min{minni,minnj}h_i+h_j+j-i-2\times min\{minn_i,minn_j\}

那么就是dist(i,j)={hi+hj+ji2×minni(minni<minnj)hi+hj+ji2×minnj(minniminnj)dist(i,j)=\begin{cases} h_i+h_j+j-i-2\times minn_i&(minn_i<minn_j)\\ h_i+h_j+j-i-2\times minn_j&(minn_i\geq minn_j) \end{cases}

那么我们把式子拆开,把与ii有关的放在一起,与jj有关的放在一起。

那么我们要统计lmidl-mid中的点对mid+1rmid+1-r中的点的答案的贡献

其实就是用kk去减一下,然后维护结果个数的后缀和就行了,这个树状数组和平衡树都可以做。

要查询的时候就直接按照后缀和查就行了。

其实这里就是cdqcdq的思想,我们将所有点按照minnminn排序,然后对于我们要统计的部分查询,否则修改就行了。

注意要处理两种情况,每种两种方向,一共四种情况。


代码:

#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;
}

vector<int> all,bit;
inline void init(){
	sort(all.begin(),all.end());
	all.erase(unique(all.begin(),all.end()),all.end());
	bit.assign(all.size()+1,0);
}
inline void add(int pos){
	pos=upper_bound(all.begin(),all.end(),pos)-all.begin();
	for(;pos;pos-=(pos&(-pos)))++bit[pos];
}
inline int query(int pos){
	re int res=0;
	pos=lower_bound(all.begin(),all.end(),pos)-all.begin()+1;
	for(;pos<bit.size();pos+=(pos&(-pos)))res+=bit[pos];
	return res;
}

struct node{
	int val,pos;
	node(cs int &_pos=0,cs int &_val=0):pos(_pos),val(_val){}
	friend bool operator<(cs node &a,cs node &b){
		return a.val<b.val;
	}
};
cs int N=100005;
int ans[N],h[N],minn[N],n,k;

inline void solve(int l,int r){
	if(l==r)return (void)++ans[l];
	int mid=(l+r)>>1;
	vector<node> vec;
	
	minn[mid]=h[mid];
	for(int re i=mid-1;i>=l;--i)minn[i]=min(minn[i+1],h[i]);
	minn[mid+1]=h[mid+1];
	for(int re i=mid+2;i<=r;++i)minn[i]=min(minn[i-1],h[i]);
	
	for(int re i=l;i<=r;++i)vec.push_back(node(i,minn[i]));
	sort(vec.begin(),vec.end());
	
	all.clear();
	for(int re i=l;i<=mid;++i)all.push_back(k-h[i]+i+2*minn[i]);
	init();
	for(int re i=0;i<vec.size();++i){
		node &t=vec[i];
		if(t.pos<=mid)add(k-h[t.pos]+t.pos+2*minn[t.pos]);
		else ans[t.pos]+=query(h[t.pos]+t.pos);
	}
	
	all.clear();
	for(int re i=l;i<=mid;++i)all.push_back(k-h[i]+i);
	init();
	for(int re i=vec.size()-1;~i;--i){
		node &t=vec[i];
		if(t.pos<=mid)add(k-h[t.pos]+t.pos);
		else ans[t.pos]+=query(h[t.pos]+t.pos-minn[t.pos]*2);
	}
	
	all.clear();
	for(int re i=mid+1;i<=r;++i)all.push_back(k-h[i]-i+2*minn[i]);
	init();
	for(int re i=0;i<vec.size();++i){
		node &t=vec[i];
		if(t.pos>mid)add(k-h[t.pos]-t.pos+minn[t.pos]*2);
		else ans[t.pos]+=query(h[t.pos]-t.pos);
	}
	
	all.clear();
	for(int re i=mid+1;i<=r;++i)all.push_back(k-h[i]-i);
	init();
	for(int re i=vec.size()-1;~i;--i){
		node &t=vec[i];
		if(t.pos>mid)add(k-h[t.pos]-t.pos);
		else ans[t.pos]+=query(h[t.pos]-t.pos-minn[t.pos]*2);
	}
	solve(l,mid);
	solve(mid+1,r);
}

signed main(){
	n=getint();
	k=getint();
	for(int re i=1;i<=n;++i)h[i]=getint();
	solve(1,n);
	for(int re i=1;i<=n;++i)printf("%d ",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章