正题
这题挺巧妙的。
我一开始想的就是考虑一个点对不包含比它大一的点的区间的贡献,在考虑一个点 对包含比它大一的点的区间的贡献,这两个东西可以用一个树状数组套主席树来维护,但是死活卡在2700ms过不了,无奈之下翻看题解。
题解的做法在这里就不再赘述了:
先考虑区间[l, r]的贡献。将区间排序后共有r-l+1段,如果i和i+1同时这个区间中,那么就可以合并两段为一段。一个所以一个区间的贡献可以转化为:区间的长度-区间中相邻数值的对数。
再考虑区间[L, R]的meaty值。按照上面的思路,可以转变为:子区间的长度和-每对相邻数值在子区间中的出现次数和。
子区间的长度和为 s(R−L+1) ,s(n)=∑nl=1l⋅(n−l+1)=(n+1)∑nl=1l−∑ni=1l2 。
每对在[L, R]内的相邻数值会产生贡献:设数值i和i+1的位置为 li,ri(L≤li<ri≤R) ,那么在所有子区间中的出现次数为 (R−ri+1)⋅(li−L+1) 。每对相邻数值在子区间中的出现次数和为 ∑i(R−ri+1)⋅(li−L+1)=−(R+1)⋅(L−1)⋅∑i1+(R+1)⋅∑ili+(L−1)⋅∑iri−∑ili⋅ri ,对于四个需要求和的值分别维护就好了。
这时候注意到还有[li, ri]在[L, R]内的要求,可以用主席树把L,R当成两个维度,或者直接离线处理。
复杂度 O(nlogn)
也不就比我少个log
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
template<class IT>inline void cmin(IT &a,IT b){if(b<a)a=b;}
template<class IT>inline void cmax(IT &a,IT b){if(a<b)a=b;}
struct BIT{
long long tre[N];
inline void add(int x,long long y){for(;x<N;x+=x&-x)tre[x]+=y;}
inline long long que(int x){static long long y;y=0ll;for(;x!=0;x-=x&-x)y+=tre[x];return y;}
}t1,t2,t3,t4;
struct node{
int x,y,t;
inline node(){}
inline node(int x,int y,int t):x(x),y(y),t(t){}
inline bool operator<(const node &a)const{
return x>a.x||(x==a.x&&(y<a.y||(y==a.y&&t<a.t)));
}
}nod[N<<1];
long long ans[N];
int a[N];
int tot,n,m;
void solve(){
int l,r;
sort(nod+1,nod+tot+1);
for(int i=1;i<=tot;++i){
l=nod[i].x;
r=nod[i].y;
if(nod[i].t==0){
t1.add(r,1ll);
t2.add(r,l+1);
t3.add(r,r-1);
t4.add(r,1ll*(l+1)*(r-1));
}
else{
ans[nod[i].t]+=(t1.que(r)*l)*r-t2.que(r)*r-t3.que(r)*l+t4.que(r);
}
}
}
int main(){
int x,y,t;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&t);
a[t]=i;
}
for(int i=1;i<n;++i){
x=a[i];y=a[i+1];
if(x>y){t=x;x=y;y=t;}
nod[++tot]=node(x,y,0);
}
for(int i=1;i<=m;++i){
scanf("%d %d",&x,&y);
t=y-x;ans[i]=1ll*(t+1)*(t+2)*(t+3)/6ll;
nod[++tot]=node(x,y,i);
}
solve();
for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
return 0;
}