古老的序列问题
题解
由于个人感觉这题部分分做法跟正解没太大关系,所以就直接讲正解了。
我们可以考虑对于原序列建立一棵线段树,将所有的询问离线下来挂树上。
那我们将询问
(
L
,
R
)
(L,R)
(L,R)挂树上时必然会有这四种可能,
- 该询问可以完全覆盖该区间,那该询问在该区间内的答案就是该区间中所有的子区间的答案,我们可以在该节点上存下有哪些询问可以完全覆盖该区间,之后就不用考虑该区间的子区间了。
- 如果该询问完全属于该区间的左子区间,那么我们可以就直接将该询问扔到左子区间去。
- 同样,如果该询问完全属于该区间的右子区间,就将它扔右子区间去。
- 如果该区间过该区间的中点,那么我们可以处理出对于该询问来说过中点的子区间的贡献,再将其往左右区间扔。
很明显,由于线段树上一个询问只会被分成
log
n
\log\,n
logn个区间,所以第一种可能与第四种可能也是
log
n
\log\,n
logn级别的。
我们考虑如何求出过中点的子区间的贡献。
我们可以考虑用指针扫每个左端点,维护每个右端点的贡献。
我们可以将右端点到中点的区间分成三种情况,
- 最大值与最小值全在左边。
- 最大值与最小值中的一个在左边。
- 最大值与最小值全在右边。
很明显,这三个部分的区分点应该是左边最大值小于右边最大值的分界点与左边最小值大于右边最小值的分界点。
这两个分界点都是随着左边界左移不断向右移动的。
所以我们可以在指针不断扫左端点时,同时用过指针维护这两个分界点。
这两个分界点之前的部分是情况
1
1
1,中间的部分是情况
2
2
2,后面的部分是情况
3
3
3。
我们用
l
m
x
lmx
lmx和
l
m
n
lmn
lmn表示左边的最大值与最小值,
r
m
x
rmx
rmx和
r
m
n
rmn
rmn表示右边的最大值与最小值。
对于情况
1
1
1,左端点的贡献是完整的,是
l
m
x
×
l
m
n
lmx\times lmn
lmx×lmn,右边的贡献为
1
1
1。
对于情况
2
2
2,左端点的贡献只有最大值与最小值,贡献位
l
m
x
/
l
m
n
lmx/lmn
lmx/lmn,右边的贡献位是相对的,为
r
m
n
/
r
m
x
rmn/rmx
rmn/rmx。
对于情况
3
3
3,左端点的贡献就只有
1
1
1了,右端点的贡献位
r
m
x
×
r
m
n
rmx\times rmn
rmx×rmn。
很明显,对于每个右端点,它的
r
m
n
,
r
m
x
rmn,rmx
rmn,rmx是固定的,而随着左端点的移动,左端点的贡献会发生变化。
而左端点的贡献时分成我们以上形容的三个区间,每个区间对应的右端点贡献情况不同,而且左端点的贡献也不同。
我们可以把它看成一个系数的形式,我们在不断区间加改变系数。
我们将情况
2
2
2的
r
m
x
rmx
rmx和
r
m
n
rmn
rmn分开,总共根据这四种形式建立
4
4
4棵线段树,维护右端点四种不同情况的系数。
我们将覆盖该区间
m
i
d
mid
mid的询问按左端点排序,当扫到该左端点时查询
m
i
d
mid
mid到右端点的那一段区间和就行了。
注意我们线段树上维护的其实是历史的和,每个左端点对其的贡献都加上去了的。
在线段树上将该节点子节点的和全部加上去就可以的到该节点的区间的所有子区间的贡献和了,也就是我们上面说的完全覆盖区间的贡献。
这样把所有询问挂上去再跑一跑就能过了。
按妹妹的说法这东西叫猫树分治~~,虽然我并未听说过这东西~~,不过其实就是个普通的分治吧。
时间复杂度
O
(
(
n
+
m
)
log
2
n
)
O\left((n+m)\log^2\,n\right)
O((n+m)log2n)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int inv2=5e8+4;
const int jzm=2333;
const int lim=1e9;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,a[MAXN],ans[MAXN],mink[MAXN],maxk[MAXN],summ[MAXN<<2];
struct ming{int l,r,id;};
bool cmp(ming x,ming y){return x.l>y.l;}
vector<ming>vec[MAXN<<2];
vector<int>ful[MAXN<<2];
struct Array{
int a[4];Array(){a[0]=a[1]=a[2]=a[3]=0;}
int& operator [](const int x){return a[x];}
};
class SegmentTree{
private:
int sum[MAXN<<2],num[MAXN<<2];Array val[MAXN<<2],siz[MAXN<<2];
public:
void build(int rt,int l,int r){
val[rt][0]=val[rt][1]=val[rt][2]=val[rt][3]=num[rt]=sum[rt]=0;
if(l==r){
siz[rt][0]=1;siz[rt][3]=1ll*mink[l]*maxk[l]%mo;
siz[rt][1]=mink[l];siz[rt][2]=maxk[l];return ;
}
int mid=l+r>>1;build(lson,l,mid);build(rson,mid+1,r);
for(int i=0;i<4;i++)siz[rt][i]=add(siz[lson][i],siz[rson][i],mo);
}
void modify(int rt,int l,int r,int al,int ar,int aw,int at){
if(l>r||l>ar||r<al||al>ar)return ;int mid=l+r>>1;
if(al<=l&&r<=ar){
const int tp=1ll*aw*siz[rt][at]%mo;
sum[rt]=add(sum[rt],tp,mo);num[rt]=add(num[rt],tp,mo);
val[rt][at]=add(val[rt][at],aw,mo);return ;
}
if(al<=mid)modify(lson,l,mid,al,ar,aw,at);
if(ar>mid)modify(rson,mid+1,r,al,ar,aw,at);
sum[rt]=add(num[rt],add(sum[lson],sum[rson],mo),mo);
}
int query(int rt,int l,int r,int al,int ar,Array aw){
if(l>r||l>ar||r<al||al>ar)return 0;int mid=l+r>>1,res=0;
if(al<=l&&r<=ar){
for(int i=0;i<4;i++)res=add(res,1ll*aw[i]*siz[rt][i]%mo,mo);
res=add(res,sum[rt],mo);return res;
}
for(int i=0;i<4;i++)aw[i]=add(aw[i],val[rt][i],mo);
if(al<=mid)res=add(res,query(lson,l,mid,al,ar,aw),mo);
if(ar>mid)res=add(res,query(rson,mid+1,r,al,ar,aw),mo);
return res;
}
}T;
void insert(int rt,int l,int r,int al,int ar,int ai){
if(l>r||l>ar||r<al)return ;int mid=l+r>>1;
if(al<=l&&r<=ar){ful[rt].pb(ai);return ;}
if(al<=mid&&mid<ar)vec[rt].pb((ming){max(al,l),min(ar,r),ai});
if(al<=mid)insert(lson,l,mid,al,ar,ai);
if(ar>mid)insert(rson,mid+1,r,al,ar,ai);
}
void sakura(int rt,int l,int r){
if(l==r){
int tp=summ[rt]=1ll*a[l]*a[l]%mo;
for(int i=0;i<ful[rt].size();i++){int t=ful[rt][i];ans[t]=add(ans[t],tp,mo);}
return ;
}
int mid=l+r>>1;mink[mid+1]=maxk[mid+1]=a[mid+1];
for(int i=mid+2;i<=r;i++)mink[i]=min(a[i],mink[i-1]),maxk[i]=max(maxk[i-1],a[i]);
T.build(1,mid+1,r);int maxx=a[mid+1],minn=a[mid+1],jx=mid+1,jn=mid+1;
sort(vec[rt].begin(),vec[rt].end(),cmp);int k=0,siz=vec[rt].size();
for(int i=mid,tmx=a[mid],tmn=a[mid];i>=l;i--){
tmx=max(a[i],tmx);tmn=min(a[i],tmn);
while(jx<=r&&maxx<tmx)jx++,maxx=max(maxx,a[jx]);
while(jn<=r&&minn>tmn)jn++,minn=min(minn,a[jn]);
T.modify(1,mid+1,r,mid+1,min(jx,jn)-1,1ll*tmx*tmn%mo,0);
T.modify(1,mid+1,r,jn,jx-1,tmx,1);
T.modify(1,mid+1,r,jx,jn-1,tmn,2);
T.modify(1,mid+1,r,max(jn,jx),r,1,3);
while(k<siz&&vec[rt][k].l==i){
int td=vec[rt][k].id,pr=vec[rt][k].r;k++;
ans[td]=add(T.query(1,mid+1,r,mid+1,pr,Array()),ans[td],mo);
}
}
summ[rt]=T.query(1,mid+1,r,mid+1,r,Array());
sakura(lson,l,mid);sakura(rson,mid+1,r);
summ[rt]=add(summ[rt],summ[lson],mo);
summ[rt]=add(summ[rt],summ[rson],mo);
for(int i=0;i<ful[rt].size();i++){int t=ful[rt][i];ans[t]=add(ans[t],summ[rt],mo);}
}
signed main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
read(n);read(m);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1,L,R;i<=m;i++)read(L),read(R),insert(1,1,n,L,R,i);
sakura(1,1,n);for(int i=1;i<=m;i++)print(ans[i]),putchar('\n');
return 0;
}