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

传送门


解析:

这道题真的就是防 A K AK AK题了,但是对于我这个蒟蒻来说三道题都是防 A K AK AK题,为什么说这道题是防 A K AK AK题呢,主要是为了阻止 l d x o i ldxoi ldxoiAK的步伐。

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

思路:

有点 c d q cdq cdq分治的味道,但不是 c d q cdq cdq分治。。。

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

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

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

然后递归处理 l − m i d l-mid lmid m i d + 1 − r mid+1-r mid+1r两个区间,显然这样处理是不重不漏的。

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

定义 m i n n i = { m i n { h i , h i + 1 . . . h m i d } ( i ≤ m i d ) m i n { h i , h i − 1 . . . h m i d + 1 } ( i ≥ m i d ) minn_i=\begin{cases} min\{h_i,h_{i+1}...h_{mid}\}&amp; (i\leq mid)\\ min\{h_i,h_{i-1}...h_{mid+1}\}&amp; (i\geq mid) \end{cases} minni={min{hi,hi+1...hmid}min{hi,hi1...hmid+1}(imid)(imid)

那么 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 } h_i+h_j+j-i-2\times min\{minn_i,minn_j\} hi+hj+ji2×min{minni,minnj}

那么就是 d i s t ( i , j ) = { h i + h j + j − i − 2 × m i n n i ( m i n n i &lt; m i n n j ) h i + h j + j − i − 2 × m i n n j ( m i n n i ≥ m i n n j ) dist(i,j)=\begin{cases} h_i+h_j+j-i-2\times minn_i&amp;(minn_i&lt;minn_j)\\ h_i+h_j+j-i-2\times minn_j&amp;(minn_i\geq minn_j) \end{cases} dist(i,j)={hi+hj+ji2×minnihi+hj+ji2×minnj(minni<minnj)(minniminnj)

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

那么我们要统计 l − m i d l-mid lmid中的点对 m i d + 1 − r mid+1-r mid+1r中的点的答案的贡献

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

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

其实这里就是 c d q cdq cdq的思想,我们将所有点按照 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;
}

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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值