【Ynoi2014】置身天上之森
分块。
题目大意:
给定一棵线段树,每个点的值是其覆盖的这段区间的所有数的和,初始全为 $0$。要支持两个操作:
- 给定 $l,r,a$,将位置在 $[l,r]$ 内的所有数的值加上 $a$,线段树上其他结点的值相应变化。
- 给定 $l,r,a$,求有多少个线段树结点,被 $[l,r]$ 完全包含,且值小于等于 $a$。
题解:
在太阳西斜的这个世界里,置身天上之森。等这场战争结束之后,不归之人与望眼欲穿的众人, 人人本着正义之名,长存不灭的过去、逐渐消逝的未来。我回来了,纵使日薄西山,即便看不到未来,此时此刻的光辉,盼君勿忘。————世界上最幸福的女孩
这题要用到线段树的性质。
对于一棵线段树,它同一个高度的所有结点,最多只有 22 种不同的区间大小。
那么整棵线段树只有 $O(\log n)$ 个不同的区间大小。
我们考虑分开维护这 $O(\log n)$ 个区间。然后容易将问题转化为这样的一个问题:
给定一个长度为 $n$ 的序列,支持区间加,区间查询一个数的排名。
我们考虑解决这个经典的问题。
考虑序列分块,然后块内元素从小到大排序。设块大小为 $S$。
对于查询操作,区间里的可以直接二分,单次查询时间复杂度 $O(nS\cdot logn)$;边角的暴力查询,单次时间复杂度 $O(S)$。
对于修改操作,区间里的可以直接打修改标记,单次修改时间复杂度 $O(nS)$;边角的可以归并,单次时间复杂度 $O(S)$。
当 $S=\sqrt {n\log n}$ 时,总体最优,为单次 $O(\sqrt{n\log n})$。空间复杂度 $O(n)$。
考虑回到原问题。我们相当于对每层线段树结点,都要做上面的问题。
那么这样做的时间复杂度是 $O(m\sqrt{n\log n}+m\sqrt{\frac n 2\log\frac n 2}+m\sqrt{\frac n 4\log\frac n 4}+\cdots)=O(m\sqrt {n\log n})$。
目前足以通过此题。
Code:
#include<cstdio>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
#define bel(x)((x-1)/B+1)
#define _L(x)(((x)-1)*B+1)
#define _R(x)((x)*B)
using namespace std;
typedef long long LL;
const int N=1e5+5;
inline int readint(){
int c=getchar(),d=0,f=0;
for(;!isdigit(c);c=getchar())f=c=='-';
for(;isdigit(c);c=getchar())d=d*10+(c^'0');
return f?-d:d;
}
int topA,topB;
pair<LL,int>A[N],C[N];
struct DataStructure{
int*d,n,B,blocks,tp,lth;
LL*tag;
pair<LL,int>*b;
inline void init(){
B=sqrt(n*log(n))+1;B*=0.2;
if(B<10)B=10;
blocks=bel(n);
b=new pair<LL,int>[n+2],d=new int[n+2];
tag=new LL[blocks+2],tp=0;
for(int i=0;i<=n+1;++i)d[i]=0,b[i]=make_pair(0LL,i);
for(int i=0;i<=blocks+1;++i)tag[i]=0;
}
inline void merge(pair<LL,int>*t){
int it0=1,it1=1;
while(it0<=topA&&it1<=topB)if(A[it0].first<C[it1].first)*t++=A[it0++];else*t++=C[it1++];
while(it0<=topA)*t++=A[it0++];
while(it1<=topB)*t++=C[it1++];
}
void modify_(int l,int r,LL x){
const int L=_L(bel(l)),R=min(_R(bel(r)),n);
topA=topB=0;
for(int i=L;i<=R;++i)
if(l<=b[i].second&&b[i].second<=r)A[++topA]=make_pair(b[i].first+x,b[i].second);
else C[++topB]=b[i];
merge(b+L);
}
inline void modify_ALL(int l,int r,LL x){
const int bL=bel(l),bR=bel(r);
if(bL==bR)modify_(l,r,x);else{
modify_(l,_R(bL),x);
modify_(_L(bR),r,x);
for(int i=bL+1;i<bR;++i)tag[i]+=x;
}
}
inline void modify_one(int t,LL x){
const int L=_L(bel(t)),R=min(_R(bel(t)),n);
pair<LL,int>k;bool ok=0;
if(x>=0){
for(int i=L;i<=R;++i)if(!ok){
if(b[i].second==t)ok=1,k=make_pair(b[i].first+x,b[i].second),--i;
}else if(i==R||b[i+1].first>k.first){b[i]=k;break;}else b[i]=b[i+1];
}else{
for(int i=R;i>=L;--i)if(!ok){
if(b[i].second==t)ok=1,k=make_pair(b[i].first+x,b[i].second),++i;
}else if(i==L||b[i-1].first<k.first){b[i]=k;break;}else b[i]=b[i-1];
}
}
void modify(int l,int r,int a){
int L=upper_bound(d+1,d+n+1,l)-d-1;
int R=lower_bound(d+1,d+n+1,r-lth+1)-d;
if(L+1<=R-1)modify_ALL(L+1,R-1,(LL)a*lth);
if(L&&R<=n&&L==R&&d[L]+lth-1>=l&&d[R]<=r){
modify_one(L,(LL)a*(r-l+1));
}else{
if(L&&d[L]+lth-1>=l)modify_one(L,(LL)a*(d[L]+lth-l));
if(R<=n&&d[R]<=r)modify_one(R,(LL)a*(r-d[R]+1));
}
}
inline int query_(int l,int r,int x){
const int bl=bel(l),L=_L(bl),R=min(_R(bl),n);
if(b[L].first+tag[bl]>x)return 0;
int res=0;
for(int i=L;i<=R;++i)
if(l<=b[i].second&&b[i].second<=r&&b[i].first+tag[bl]<=x)++res;
return res;
}
inline int query_ALL(int l,int r,int x){
const int bl=bel(l);
if(b[l].first+tag[bl]>x)return 0;
if(b[r].first+tag[bl]<=x)return r-l+1;
int it=upper_bound(b+l,b+r+1,make_pair(x-tag[bl],114514))-b-l;
return it;
}
int query(int l,int r,int x){
int L=lower_bound(d+1,d+n+1,l)-d;
int R=upper_bound(d+1,d+n+1,r-lth+1)-d-1;
if(L<=R){
int res=0;
const int bL=bel(L),bR=bel(R);
if(bL==bR)res=query_(L,R,x);else{
res=query_(L,_R(bL),x)+query_(_L(bR),R,x);
for(int i=bL+1;i<bR;++i)res+=query_ALL(_L(i),min(_R(i),n),x);
}
return res;
}else return 0;
}
}G[40];
int n,m,bid[N],nod;
inline void __(int l,int r){
if(!bid[r-l+1])bid[r-l+1]=++nod,G[nod].lth=r-l+1;
++G[bid[r-l+1]].n;
if(l<r){
const int mid=l+r>>1;
__(l,mid),__(mid+1,r);
}
}
inline void build(int l,int r){
G[bid[r-l+1]].d[++G[bid[r-l+1]].tp]=l;
if(l<r){
const int mid=l+r>>1;
build(l,mid),build(mid+1,r);
}
}
int main(){
ios::sync_with_stdio(0),cout.tie(0);
n=readint(),m=readint();
__(1,n);
for(int i=1;i<=nod;++i)G[i].init();
build(1,n);
while(m--){
int op=readint(),l=readint(),r=readint(),a=readint();
if(op==1){
for(int i=1;i<=nod;++i)
G[i].modify(l,r,a);
}else{
int ans=0;
for(int i=1;i<=nod;++i)
ans+=G[i].query(l,r,a);
cout<<ans<<'\n';
}
}
return 0;
}