题面
题解
考虑把原序列每 \(k\) 位分成一段,然后对于每一段维护起点在这一段中的最小值。
先考虑询问,对于起点在中间的整段我们直接线段树查区间最小值,现在考虑两边的小段。以左边的小段为例:(不妨假设它在第一段,在其他段是相同的)
如图,红框内是我们起点的可选区域,那么蓝框内就是我们终点的选择区域。
起点为 \(i\) 的答案为 \(\max(A_i,\cdots,A_{i+k-1})\),它可以分为三段:红阴影、绿阴影、蓝阴影,可以看出绿阴影部分是不变的。又注意到 \(i\) 增大红阴影部分单调不增,蓝阴影部分单调不降,于是这就可以二分。
然后就能看出以 \(k\) 分段的好处了:如果我们对于每一段都用一棵线段树来维护的话,那么红框和蓝框部分在线段树上是同构的,而且红阴影和蓝阴影是互补的,那么我们可以在线段树上维护区间最大值,然后在线段树上二分并得到小段的答案。
那么现在考虑修改,我们需要维护整段的答案,以及区间最大值。(后者很好维护,略去)
如图,假设把 \([l,r]\) 区间加 \(d\),其中 \(l\) 在第 \(bl\) 段,\(r\) 在第 \(br\) 段。对于 \(bl< i< br-1\),第 \(i\) 段和第 \(i+1\) 段都区间加了 \(d\),那么起点在第 \(i\) 段的答案直接加 \(d\) 即可。而对于起点在第 \(bl-1,bl,br-1,br\) 段的情况,我们把它们当成四个小段扔去询问,得到答案后再在线段树上单点修改这四个位置的值即可。
时间复杂度 \(O(q\log n)\)。
#include<bits/stdc++.h>
#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
#define INF 0x7fffffff
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
struct data
{
int u,l,r;
data(){};
data(int a,int b,int c){u=a,l=b,r=c;}
};
int T,nn,n,block,k,q;
int getB(int x){return (x-1)/k+1;}
int getL(int x){return (x-1)*k+1;}
int getR(int x){return x*k;}
namespace Seg1//大块ans
{
const int N=10000010;
int node,rt,ch[N][2],ans[N],lazy[N];
int newnode()
{
int u=++node;
ans[u]=lazy[u]=lc(u)=rc(u)=0;
return u;
}
void up(int u)
{
ans[u]=min(ans[lc(u)],ans[rc(u)]);
}
void downn(int &u,int d)
{
if(!u) u=newnode();
ans[u]+=d,lazy[u]+=d;
}
void down(int u)
{
if(lazy[u])
{
downn(lc(u),lazy[u]);
downn(rc(u),lazy[u]);
lazy[u]=0;
}
}
void update1(int &u,int l,int r,int ql,int qr,int d)
{
if(!u) u=newnode();
if(ql<=l&&r<=qr)
{
downn(u,d);
return;
}
down(u);
int mid=(l+r)>>1;
if(ql<=mid) update1(lc(u),l,mid,ql,qr,d);
if(qr>mid) update1(rc(u),mid+1,r,ql,qr,d);
up(u);
}
void init()
{
node=rt=0;
if(nn%k) update1(rt,1,block,block,block,INF);
}
void update2(int &u,int l,int r,int x,int y)
{
if(!u) u=newnode();
if(l==r)
{
ans[u]=y;
return;
}
down(u);
int mid=(l+r)>>1;
if(x<=mid) update2(lc(u),l,mid,x,y);
else update2(rc(u),mid+1,r,x,y);
up(u);
}
int query(int u,int l,int r,int ql,int qr)
{
if(!u) return 0;
if(ql<=l&&r<=qr) return ans[u];
down(u);
int mid=(l+r)>>1,res=INF;
if(ql<=mid) res=min(res,query(lc(u),l,mid,ql,qr));
if(qr>mid) res=min(res,query(rc(u),mid+1,r,ql,qr));
return res;
}
}
namespace Seg2//区间maxn
{
const int N=10000010;
int node,Rt,ch[N][2],rt[N];
int maxn[N],lazy[N];
int newnode()
{
int u=++node;
maxn[u]=lazy[u]=rt[u]=lc(u)=rc(u)=0;
return u;
}
void up(int u)
{
maxn[u]=max(maxn[lc(u)],maxn[rc(u)]);
}
void downn(int &u,int d)
{
if(!u) u=newnode();
maxn[u]+=d,lazy[u]+=d;
}
void down(int u)
{
if(lazy[u])
{
downn(lc(u),lazy[u]);
downn(rc(u),lazy[u]);
lazy[u]=0;
}
}
void up2(int u)
{
maxn[u]=maxn[rt[u]];
}
void down2(int u)
{
if(lazy[u])
{
downn(rt[u],lazy[u]);
lazy[u]=0;
}
}
void update2(int &u,int l,int r,int ql,int qr,int d)
{
if(!u) u=newnode();
if(ql<=l&&r<=qr)
{
downn(u,d);
return;
}
down(u);
int mid=(l+r)>>1;
if(ql<=mid) update2(lc(u),l,mid,ql,qr,d);
if(qr>mid) update2(rc(u),mid+1,r,ql,qr,d);
up(u);
}
void update1(int &u,int l,int r,int ql,int qr,int d)
{
if(!u) u=newnode();
if(ql<=getL(l)&&getR(r)<=qr)
{
downn(u,d);
return;
}
if(l==r)
{
down2(u);
update2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1),d);
up2(u);
return;
}
down(u);
int mid=(l+r)>>1,midpos=getR(mid);
if(ql<=midpos) update1(lc(u),l,mid,ql,qr,d);
if(qr>midpos) update1(rc(u),mid+1,r,ql,qr,d);
up(u);
}
void init()
{
node=Rt=0;
update1(Rt,1,block,nn+1,n,INF);
}
int query2(int u,int l,int r,int ql,int qr)
{
if(!u) return 0;
if(ql<=l&&r<=qr) return maxn[u];
down(u);
int mid=(l+r)>>1,ans=-INF;
if(ql<=mid) ans=max(ans,query2(lc(u),l,mid,ql,qr));
if(qr>mid) ans=max(ans,query2(rc(u),mid+1,r,ql,qr));
return ans;
}
int query1(int u,int l,int r,int ql,int qr)
{
if(!u) return 0;
if(ql<=getL(l)&&getR(r)<=qr) return maxn[u];
if(l==r)
{
down2(u);
return query2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1));
}
down(u);
int mid=(l+r)>>1,midpos=getR(mid),ans=-INF;
if(ql<=midpos) ans=max(ans,query1(lc(u),l,mid,ql,qr));
if(qr>midpos) ans=max(ans,query1(rc(u),mid+1,r,ql,qr));
return ans;
}
void find2(int u,int l,int r,int ql,int qr,vector<data> &p)
{
if(ql<=l&&r<=qr)
{
p.push_back(data(u,l,r));
return;
}
down(u);
int mid=(l+r)>>1;
if(ql<=mid) find2(lc(u),l,mid,ql,qr,p);
if(qr>mid) find2(rc(u),mid+1,r,ql,qr,p);
}
void find1(int u,int l,int r,int ql,int qr,vector<data> &p)
{
if(l==r)
{
down2(u);
find2(rt[u],1,k,ql-getR(l-1),qr-getR(l-1),p);
return;
}
down(u);
int mid=(l+r)>>1,midpos=getR(mid);
if(ql<=midpos) find1(lc(u),l,mid,ql,qr,p);
if(qr>midpos) find1(rc(u),mid+1,r,ql,qr,p);
}
int getp(int u1,int u2,int l,int r,int max1,int max2)
{
if(l==r) return l;
down(u1),down(u2);
int mid=(l+r)>>1;
if(max(max2,maxn[lc(u2)])>max(max1,maxn[rc(u1)]))
return getp(lc(u1),lc(u2),l,mid,max(max1,maxn[rc(u1)]),max2);
return getp(rc(u1),rc(u2),mid+1,r,max1,max(max2,maxn[lc(u2)]));
}
}
int getans(int p)
{
return Seg2::query1(Seg2::Rt,1,block,p,p+k-1);
}
int small(int l1,int r1)
{
static vector<data>p1,p2;
static int tot,sum1[50],sum2[50];
if(l1==r1) return getans(l1);
r1--;
int l2=l1+k,r2=r1+k;
p1.resize(1),p2.resize(1);
Seg2::find1(Seg2::Rt,1,block,l1,r1,p1);
Seg2::find1(Seg2::Rt,1,block,l2,r2,p2);
assert(p1.size()==p2.size());
tot=p1.size()-1;
sum1[tot+1]=-INF;
for(int i=tot;i>=1;i--) sum1[i]=max(sum1[i+1],Seg2::maxn[p1[i].u]);
sum2[0]=-INF;
for(int i=1;i<=tot;i++) sum2[i]=max(sum2[i-1],Seg2::maxn[p2[i].u]);
int divide=-1;
for(int i=1;i<=tot;i++)
{
if(sum1[i+1]<sum2[i])
{
divide=i;
break;
}
}
int pos=Seg2::getp(p1[divide].u,p2[divide].u,p1[divide].l,p1[divide].r,sum1[divide+1],sum2[divide-1]);
return min(getans(getR(getB(l1)-1)+pos+1),getans(getR(getB(l1)-1)+pos));
}
void remake(int b)
{
if(b<1) return;
if(b==block)
{
if(!(nn%k))
Seg1::update2(Seg1::rt,1,block,block,getans(getL(block)));
return;
}
int tmp=small(getL(b),getR(b));
Seg1::update2(Seg1::rt,1,block,b,tmp);
}
void change(int l,int r,int d)
{
int bl=getB(l),br=getB(r);
Seg2::update1(Seg2::Rt,1,block,l,r,d);
if(bl+1<=br-2) Seg1::update1(Seg1::rt,1,block,bl+1,br-2,d);
remake(bl-1),remake(bl),remake(br-1),remake(br);
}
int ask(int l,int r)
{
int bl=getB(l),br=getB(r);
if(bl==br) return small(l,r);
int ans=INF;
if(bl+1<=br-1) ans=min(ans,Seg1::query(Seg1::rt,1,block,bl+1,br-1));
ans=min(ans,small(l,getR(bl)));
ans=min(ans,small(getL(br),r));
return ans;
}
int main()
{
// freopen("sequence3.in","r",stdin);
// freopen("sequence3_my.out","w",stdout);
T=read();
while(T--)
{
nn=n=read(),k=read(),q=read();
n=k*(int)ceil(1.0*n/k),block=n/k;
Seg1::init(),Seg2::init();
while(q--)
{
int opt=read(),l=read(),r=read();
if(opt==1)
{
int d=read();
change(l,r,d);
}
else printf("%d\n",ask(l,r-k+1));
}
}
return 0;
}
标签:qr,XSY4191,分块,int,sequence,mid,maxn,rc,ql
From: https://www.cnblogs.com/ez-lcw/p/16842979.html