[naval International Program Office] Ancient sequence problem

Ancient sequence problem

Problem solution

As I personally feel that the partial division of this problem has little to do with the positive solution, I will directly talk about the positive solution.
We can consider building a segment tree for the original sequence and hanging all queries offline on the tree.
Then we'll ask ( L , R ) (L,R) (L,R) there must be these four possibilities when hanging on a tree,

  • The query can completely cover the interval, and the answer of the query in the interval is the answer of all sub intervals in the interval. We can save which queries can completely cover the interval on this node, and then we don't need to consider the sub intervals of the interval.
  • If the query completely belongs to the left sub interval of the interval, we can directly throw the query to the left sub interval.
  • Similarly, if the query completely belongs to the right sub interval of the interval, throw it to the right sub interval.
  • If the interval passes the midpoint of the interval, we can process the contribution of the sub interval that said the midpoint for the query, and then throw it to the left and right intervals.

Obviously, because a query on the line segment tree will only be divided into log ⁡   n \log\,n logn is an interval, so the first possibility and the fourth possibility are the same log ⁡   n \log\,n logn level.
We consider how to find the contribution of subintervals passing through the midpoint.
We can consider scanning each left endpoint with a pointer to maintain the contribution of each right endpoint.
We can divide the interval from the right endpoint to the midpoint into three cases,

  • The maximum and minimum values are all on the left.
  • One of the maximum and minimum values is on the left.
  • The maximum and minimum values are all on the right.

Obviously, the distinguishing points of these three parts should be the dividing point where the maximum value on the left is less than the maximum value on the right and the dividing point where the minimum value on the left is greater than the minimum value on the right.
These two demarcation points move continuously to the right as the left boundary moves to the left.
Therefore, we can use the pointer to maintain the two demarcation points at the same time when the pointer constantly sweeps the left endpoint.
The part before these two demarcation points is the situation 1 1 1. The middle part is the situation 2 2 2. The latter part is the situation 3 3 3.
We use l m x lmx lmx and l m n lmn lmn represents the maximum and minimum values on the left, r m x rmx rmx and r m n rmn rmn represents the maximum and minimum values on the right.
For the situation 1 1 1. The contribution of the left endpoint is complete l m x × l m n lmx\times lmn lmx × lmn, the contribution on the right is 1 1 1.
For the situation 2 2 2. The contribution of the left end point has only the maximum and minimum, and the contribution bit l m x / l m n lmx/lmn lmx/lmn, the contribution bit on the right is relative, and r m n / r m x rmn/rmx rmn/rmx.
For the situation 3 3 3. The contribution of the left endpoint is only 1 1 1, the contribution bit of the right endpoint r m x × r m n rmx\times rmn rmx×rmn.
Obviously, for each right endpoint, its r m n , r m x rmn,rmx Rmn and Rmx are fixed, and the contribution of the left endpoint will change with the movement of the left endpoint.
The contribution of the left endpoint is divided into the three intervals described above. The contribution of the right endpoint corresponding to each interval is different, and the contribution of the left endpoint is also different.
We can think of it as a form of coefficients, and we add changing coefficients in constant intervals.
We will the situation 2 2 2 r m x rmx rmx and r m n rmn rmn is separated and established according to these four forms in total 4 4 4 segment trees to maintain the coefficients of four different cases at the right endpoint.
We will cover this interval m i d mid The query of mid is sorted by the left endpoint. When the left endpoint is scanned, the query is performed m i d mid The interval and from mid to the right endpoint are OK.
Note that what we maintain on the segment tree is actually a historical sum, and the contribution of each left endpoint to it is added.
By adding the sum of all the sub nodes of the node to the segment tree, we can get the contribution sum of all the sub intervals of the node's interval, that is, the contribution of the complete coverage interval mentioned above.
So hang up all the inquiries and run again.

According to my sister, this thing is called cat tree divide and conquer ~ ~. Although I haven't heard of it ~ ~, it's actually an ordinary divide and conquer.
Time complexity O ( ( n + m ) log ⁡ 2   n ) O\left((n+m)\log^2\,n\right) O((n+m)log2n).

Source code

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

Tags: Algorithm

Posted on Mon, 04 Oct 2021 22:54:13 -0400 by JCScoobyRS