传送门
解析:
这道题真的就是防AKAKAK题了,但是对于我这个蒟蒻来说三道题都是防AKAKAK题,为什么说这道题是防AKAKAK题呢,主要是为了阻止ldxoildxoildxoiAK的步伐。
蒟蒻考场上打了30pts30pts30pts暴力靠着常数优化勉强卡了60pts60pts60pts,垫底滚粗了。。。
思路:
有点cdqcdqcdq分治的味道,但不是cdqcdqcdq分治。。。
首先iii的房顶到jjj的房顶(i<j)(i<j)(i<j)的距离是hi+hj+j−i−2×min{hi,hi+1...hj}h_i+h_j+j-i-2\times min\{h_i,h_{i+1}...h_j\}hi+hj+j−i−2×min{hi,hi+1...hj}。其实很好理解,我们必须先走到最低的地方绕过去才能过去。
显然我们计算能否从iii的房顶达到jjj的房顶可以O(n)O(n)O(n),如果预处理区间最小值可以O(1)O(1)O(1)。但是要统计所有的话这个就显得有点吃力了。
那么考虑分治,每次选取一个区间<l,r><l,r><l,r>,以中间点mid=(l+r)/2mid=(l+r)/2mid=(l+r)/2为分界线统计每个l−midl-midl−mid中的点能够到达多少在mid+1−rmid+1-rmid+1−r中的点,反过来也是一样的。
然后递归处理l−midl-midl−mid和mid+1−rmid+1-rmid+1−r两个区间,显然这样处理是不重不漏的。
然后就是处理的重头戏了。
定义minni={min{hi,hi+1...hmid}(i≤mid)min{hi,hi−1...hmid+1}(i≥mid)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}minni={min{hi,hi+1...hmid}min{hi,hi−1...hmid+1}(i≤mid)(i≥mid)
那么iii和jjj的距离就是hi+hj+j−i−2×min{minni,minnj}h_i+h_j+j-i-2\times min\{minn_i,minn_j\}hi+hj+j−i−2×min{minni,minnj}
那么就是dist(i,j)={hi+hj+j−i−2×minni(minni<minnj)hi+hj+j−i−2×minnj(minni≥minnj)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}dist(i,j)={hi+hj+j−i−2×minnihi+hj+j−i−2×minnj(minni<minnj)(minni≥minnj)
那么我们把式子拆开,把与iii有关的放在一起,与jjj有关的放在一起。
那么我们要统计l−midl-midl−mid中的点对mid+1−rmid+1-rmid+1−r中的点的答案的贡献
其实就是用kkk去减一下,然后维护结果个数的后缀和就行了,这个树状数组和平衡树都可以做。
要查询的时候就直接按照后缀和查就行了。
其实这里就是cdqcdqcdq的思想,我们将所有点按照minnminnminn排序,然后对于我们要统计的部分查询,否则修改就行了。
注意要处理两种情况,每种两种方向,一共四种情况。
代码:
#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;
}