【题解】P3246 [HNOI2016] 序列
P3246 [HNOI2016] 序列
题意
给定长度为 \(n\) 的序列。
有 \(m\) 次询问,每次询问一个区间 \([l,r]\),请求出:
\[\displaystyle \sum_{i=l}^r\sum_{j=i}^r \min_{k=i}^j a_k
\]
\(n,m\le 10^5\)。
题解
知识点:分治,线段树,扫描线。
题解里没有人使用分治,所以这里给出一个分治做法。
先考虑单次询问,设询问为 \([l,r]\),则从 \([l,r]\) 开始分治,每次取中点 \(mid\) 并递归 \([l,mid]\) 和 \([mid+1,r]\)。
假设当前分治区间为 \([l,r]\),则只计算跨过 \(mid\) 的区间的贡献。
为了统计,需要处理出从 \(mid+1\) 开始的前缀 \(a_i\) 最小值,和从 \(mid\) 开始的后缀 \(a_i\) 最小值。
由于这题可能出现相同的数,对于值相同的两个数,这里断言下标更小的更小,以防止记重或者记漏。
然后分讨最小值出现在 \(mid\) 左边还是右边,用两次双指针即可,看代码应该就看懂了。
这一部分的代码。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 102506
#define int long long
int n,m,a[N],s[N];
inline int sol(int l,int r){
if(l>r){
return 0;
}
if(l==r){
return a[l];
}
int mid=(l+r)>>1,ans=sol(l,mid)+sol(mid+1,r);
s[mid]=a[mid],s[mid+1]=a[mid+1];
per(i,l,mid-1){
s[i]=min(s[i+1],a[i]);
}
rep(i,mid+2,r){
s[i]=min(s[i-1],a[i]);
}
int j=mid+1;
rep(i,mid+1,r){
while(j>l&&s[j-1]>s[i]){
j--;
}
ans+=s[i]*(mid-j+1);
}
j=mid;
per(i,l,mid){
while(j<r&&s[j+1]>=s[i]){
j++;
}
ans+=s[i]*(j-mid);
}
return ans;
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n){
cin>>a[i];
}
rep(i,1,m){
int l,r;
cin>>l>>r;
cout<<sol(l,r)<<"\n";
}
return 0;
}
这题不强制在线,说明可以离线。
好想一次分治就把所有询问处理出来,嘻嘻。
考虑离线询问,并建出分治树,将询问“插入”树上,类似线段树的方式,每个询问最多会插入到 \(O(\log n)\) 个区间上。
最后一次分治计算出所有询问答案,具体地,对于 \([l,r]\),每一次分讨的过程中都顺便对询问的左/右端点(视情况)做扫描线,在线段树上动态维护区间贡献。
代码很易懂,这里就不细讲了。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 102506
#define int long long
int n,m,a[N],s[N],ans[N],w[N<<2];
vector<int>e[N<<2],L[N],R[N];
struct node{
int l,r;
}q[N];
struct segt{
#define mid ((l+r)>>1)
int s[N<<2],tg[N<<2];
inline void un(int k){
s[k]=s[k*2]+s[k*2+1];
}
inline void addt(int k,int l,int r,int d){
tg[k]+=d;
s[k]+=(r-l+1)*d;
}
inline void pd(int k,int l,int r){
if(tg[k]){
addt(k*2,l,mid,tg[k]);
addt(k*2+1,mid+1,r,tg[k]);
tg[k]=0;
}
}
inline void build(int k,int l,int r){
tg[k]=0;
if(l==r){
s[k]=0;
return;
}
build(k*2,l,mid);
build(k*2+1,mid+1,r);
un(k);
}
inline void upd(int L,int R,int k,int l,int r,int d){
if(L<=l&&R>=r){
addt(k,l,r,d);
return;
}
pd(k,l,r);
if(L<=mid){
upd(L,R,k*2,l,mid,d);
}
if(R>mid){
upd(L,R,k*2+1,mid+1,r,d);
}
un(k);
}
inline int ask(int L,int R,int k,int l,int r){
if(L<=l&&R>=r){
return s[k];
}
pd(k,l,r);
int ans=0;
if(L<=mid){
ans+=ask(L,R,k*2,l,mid);
}
if(R>mid){
ans+=ask(L,R,k*2+1,mid+1,r);
}
return ans;
}
#undef mid
}t;
inline void ins(int k,int l,int r,int i){
if(q[i].l<=l&&r<=q[i].r){
e[k].pb(i);
return;
}
int mid=(l+r)>>1;
if(q[i].r<=mid){
ins(k*2,l,mid,i);
return;
}
if(q[i].l>mid){
ins(k*2+1,mid+1,r,i);
return;
}
e[k].pb(i);
ins(k*2,l,mid,i);
ins(k*2+1,mid+1,r,i);
}
inline void sol(int k,int l,int r){
if(l>r){
return;
}
if(l==r){
for(int x:e[k]){
ans[x]+=a[l];
}
w[k]=a[l];
return;
}
int mid=(l+r)>>1;
sol(k*2,l,mid);
sol(k*2+1,mid+1,r);
w[k]=w[k*2]+w[k*2+1];
rep(i,l,r){
L[i].clear();
R[i].clear();
}
vector<int>tmp;
for(int x:e[k]){
if(q[x].l<=l&&r<=q[x].r){
tmp.pb(x);
continue;
}
L[max(q[x].l,l)].pb(x);
R[min(q[x].r,r)].pb(x);
}
s[mid]=a[mid],s[mid+1]=a[mid+1];
per(i,l,mid-1){
s[i]=min(s[i+1],a[i]);
}
rep(i,mid+2,r){
s[i]=min(s[i-1],a[i]);
}
t.build(1,l,r);
int j=mid+1;
rep(i,mid+1,r){
while(j>l&&s[j-1]>s[i]){
j--;
}
if(j<=mid){
t.upd(j,mid,1,l,r,s[i]);
}
for(int x:R[i]){
ans[x]+=t.ask(q[x].l,mid,1,l,r);
}
w[k]+=s[i]*(mid-j+1);
}
j=mid;
per(i,l,mid){
while(j<r&&s[j+1]>=s[i]){
j++;
}
if(j>=mid+1){
t.upd(mid+1,j,1,l,r,s[i]);
}
for(int x:L[i]){
ans[x]+=t.ask(mid+1,q[x].r,1,l,r);
}
w[k]+=s[i]*(j-mid);
}
for(int x:tmp){
ans[x]+=w[k];
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n){
cin>>a[i];
}
rep(i,1,m){
cin>>q[i].l>>q[i].r;
ins(1,1,n,i);
}
sol(1,1,n);
rep(i,1,m){
cout<<ans[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号