题目大意
给出一个随机数列
{An}
,再给出
t
组询问,每次询问左端点在
数据范围
1≤t≤40000,1≤l1≤r1≤100000,1≤l2≤r2≤100000.
解题报告
考虑离线的做法,把询问拆成
Q(r2,l1,r1)−Q(l2−1,l1,r1)
,
Q(i,l,r)
为右端点小于等于
i
,左端点在
从左往右移动右端点,当移动到位置
i
时,用单调栈找到最小的
在线段树上打标记来维护,线段上每个节点记录:
v
:(左端点在该节点范围内的)区间最新版本的最大值之和
再维护以下标记:
a
:最新版本的要修改成的值
向上合并的做法很简单:
x.s=x.lch.s+x.lch.v∗(x.t−x.lch.t)+x.rch.s+x.rch.v∗(x.t−x.rch.t)
x.v=x.lch.v+x.rch.v
因为修改操作其实也和下放标记的操作类似,我们只用考虑如何用一个标记去修改一个节点维护的数据和标记:
用标记
(a,s,tl,tr)
修改节点
x
维护的数据:
x.v=a∗len(x)
x.t=tr
用标记
(a,s,tl,tr)
修改标记
x
:
如果标记
否则:
x.a=a
x.tr=tr
之后就是简单的线段树操作了,不再赘述。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int gint(){
char c; int f=1;
while(c=getchar(),c<48||c>57)
if(c=='-')f=-1;
int x=0;
for(;c>47&&c<58;c=getchar()){
x=x*10+c-48;
}
return x*f;
}
#define max_N 100005
#define mod 1000000000
int n,A[max_N];
void init(){
int g1=1,g2=1;
for(int i=1;i<=n;++i){
g1=1ll*g1*1023%mod;
g2=1ll*g2*1025%mod;
A[i]=g1^g2;
}
}
struct QY{
int l,r,f,id,next;
QY(int l=0,int r=0,int f=0,int id=0,int n=0):l(l),r(r),f(f),id(id),next(n){}
}Q[max_N];
int t,head[max_N],tot;
inline void add_QY(int x,int l,int r,int f,int id){
if(!x)return;
Q[++tot]=QY(l,r,f,id,head[x]),head[x]=tot;
}
typedef long long ll;
struct data{
ll v,s;
int t;
}T[max_N<<2];
inline data merge(const data&a,const data&b){
data res;
res.v=a.v+b.v;
res.t=max(a.t,b.t);
res.s=a.s+b.s+a.v*(res.t-a.t)+b.v*(res.t-b.t);
return res;
}
struct tags{
int a,tl,tr;
ll s;
}tag[max_N<<2];
inline void modify(data&x,const tags&y,int len){
x.s+=x.v*(y.tl-x.t)+y.s*len;
x.v=1ll*y.a*len;
x.t=y.tr;
}
inline void modify(tags&x,const tags&y){
if(x.a==-1){x=y; return;}
x.s+=1ll*x.a*(y.tl-x.tr)+y.s;
x.a=y.a,x.tr=y.tr;
}
inline void putdown(int x,int l,int m,int r){
if(tag[x].a==-1)return;
modify(T[x<<1],tag[x],m-l+1);
modify(tag[x<<1],tag[x]);
modify(T[x<<1|1],tag[x],r-m);
modify(tag[x<<1|1],tag[x]);
tag[x].a=-1;
}
#define lch x<<1,l,m
#define rch x<<1|1,m+1,r
void modify(int x,int l,int r,int ll,int rr,int a,int t){
if(ll<=l&&r<=rr){
tags y;
y.a=a,y.s=0,y.tl=y.tr=t;
modify(T[x],y,r-l+1);
modify(tag[x],y);
return;
}
int m=(l+r)>>1;
putdown(x,l,m,r);
if(ll<=m)modify(lch,ll,rr,a,t);
if(rr>m) modify(rch,ll,rr,a,t);
T[x]=merge(T[x<<1],T[x<<1|1]);
}
data query(int x,int l,int r,int ll,int rr){
if(l==ll&&r==rr)return T[x];
int m=(l+r)>>1;
putdown(x,l,m,r);
if(rr<=m)return query(lch,ll,rr);
if(ll>m) return query(rch,ll,rr);
return merge(query(lch,ll,m),query(rch,m+1,rr));
}
int st[max_N],top;
ll ans[max_N];
void Tree_init(int x,int l,int r){
T[x].v=T[x].s=T[x].t=0;
tag[x].a=-1;
if(l==r)return;
int m=(l+r)>>1;
Tree_init(x<<1,l,m);
Tree_init(x<<1|1,m+1,r);
}
int main(){
// freopen("input.txt","r",stdin);
t=gint();
for(int i=1,l1,r1,l2,r2;i<=t;++i){
l1=gint(),r1=gint(),l2=gint(),r2=gint();
add_QY(l2-1,l1,r1,-1,i),add_QY(r2,l1,r1,1,i);
n=max(n,max(r1,r2));
}
init();
Tree_init(1,1,n);
top=0;
for(int i=1;i<=n;++i){
while(top&&A[st[top]]<=A[i])--top;
modify(1,1,n,st[top]+1,i,A[i],i);
st[++top]=i;
for(int j=head[i];j;j=Q[j].next){
data tmp=query(1,1,n,Q[j].l,Q[j].r);
ans[Q[j].id]+=(tmp.s+tmp.v*(i-tmp.t+1))*Q[j].f;
}
}
Tree_init(1,1,n);
top=0;
for(int i=1;i<=n;++i){
while(top&&A[st[top]]>=A[i])--top;
modify(1,1,n,st[top]+1,i,A[i],i);
st[++top]=i;
for(int j=head[i];j;j=Q[j].next){
data tmp=query(1,1,n,Q[j].l,Q[j].r);
ans[Q[j].id]+=(tmp.s+tmp.v*(i-tmp.t+1))*Q[j].f*-1;
}
}
for(int i=1;i<=t;++i){
printf("%lld\n",ans[i]);
}
return 0;
}