1 条题解
-
0
自动搬运
来自洛谷,原作者为

yyyyxh
OI 是啥 (O_o)?搬运于
2025-08-24 22:46:45,当前版本为作者最后更新于2023-11-23 09:49:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
发现时限这么大,考虑根号。题目相当于是对于每一个询问,你需要求出往后添加多少个询问之后有每个点的 数组对应位置超过 。可以直接序列分块,劈成一段一段后所有答案直接取个 就是原答案。
整块的话考虑离线处理出所有位置的答案,这个答案明显是有单调性的所以可以双指针。直接用线段树比较憨,发现你可以直接用分块的结构,散块的修改暴力做,整块修改直接打标记。
散块考虑对每个位置扫描线,发现是让我们支持加入删除和查询第 大,树状树组可以直接做到 。
但是插入删除第 大这玩意也是可以根号平衡的,具体地我们再维护一个数组 表示第 大的数位于哪个块内,发现插入和删除对于这个数组的影响都是 的,然后再对每个块维护第 大的数具体是哪个,这个数组可以暴力重构,于是就做到了 ,空间离线一些东西当然可以做到 。
#include <cstdio> #include <vector> #include <cmath> #include <cassert> #include <algorithm> using namespace std; int read(){ char c=getchar();int x=0; while(c<48||c>57) c=getchar(); do x=(x<<1)+(x<<3)+(c^48),c=getchar(); while(c>=48&&c<=57); return x; } const int N=100003,B=320; int n,m,sq,bn; int ql[N],qr[N]; vector<int> vec[N],ui[N],ud[N],qi[N],qd[N]; int bel[N],res[N],ans[N]; int a[N],lb[B],rb[B]; int ccbase[N<<1],*cc=ccbase+N,arr[N]; namespace Block{ int bid[N],mp[N]; int cnt[B],pre[N]; int f[B][B],msq; int mlb[N],mrb[N]; inline void init(){ msq=ceil(sqrt(m)); for(int i=1;i<=m;++i) bid[i]=(i-1)/msq+1; for(int i=1;i<=bid[m];++i){ mlb[i]=(i-1)*msq+1; mrb[i]=i*msq; } mrb[bid[m]]=m; } inline void inc(int x){ int xx=bid[x]; for(int i=x;i<=mrb[xx];++i) ++pre[i]; for(int i=mrb[xx];i>=mlb[xx];--i) f[xx][pre[i]]=i; for(int i=bid[m];i>=xx;--i) mp[++cnt[i]]=i; } inline void dec(int x){ int xx=bid[x]; for(int i=x;i<=mrb[xx];++i) --pre[i]; for(int i=mrb[xx];i>=mlb[xx];--i) f[xx][pre[i]]=i; for(int i=xx;i<=bid[m];++i) mp[cnt[i]--]=i+1; mp[cnt[bid[m]]+1]=0; } inline int qry(int x){return cnt[bid[x]-1]+pre[x];} inline int jump(int x){ if(x>=N) return m+1; if(!mp[x]) return m+1; int xx=mp[x]; x-=cnt[xx-1]; return f[xx][x]; } } bool exi[N]; int s[N],tp; int ss[N],stp; int main(){ n=read();m=read(); for(int i=1;i<=n;++i) a[i]=read(); sq=ceil(sqrt(n)); for(int i=1;i<=n;++i) bel[i]=(i-1)/sq+1; bn=bel[n]; for(int i=1;i<=bn;++i) lb[i]=(i-1)*sq+1,rb[i]=i*sq; rb[bn]=n; for(int i=1;i<=m;++i){ ql[i]=read();qr[i]=read(); vec[read()].emplace_back(i); res[i]=i; } for(int x=1;x<=bn;++x){ int tag=0,mn=0x3f3f3f3f; for(int i=lb[x];i<=rb[x];++i){ ++cc[arr[i]=-a[i]]; if(mn>arr[i]) mn=arr[i]; } for(int i=1,j=0;i<=m;++i){ while(j<=m&&tag+mn<=0){ if(++j>m) break; if(ql[j]<=lb[x]&&rb[x]<=qr[j]) ++tag; else{ int l=max(lb[x],ql[j]),r=min(rb[x],qr[j]); for(int t=l;t<=r;++t){ --cc[arr[t]]; ++cc[++arr[t]]; } if(!cc[mn]) ++mn; } } if(ql[i]<=lb[x]&&rb[x]<=qr[i]){ --tag; if(res[i]<j) res[i]=j-1; } else{ int l=max(lb[x],ql[i]),r=min(rb[x],qr[i]); for(int t=l;t<=r;++t){ --cc[arr[t]]; ++cc[--arr[t]]; } if(cc[mn-1]) --mn; } } for(int i=lb[x];i<=rb[x];++i) --cc[arr[i]=-a[i]]; } for(int i=1;i<=m;++i){ int l=ql[i],r=qr[i]; ui[l].emplace_back(i); ud[r+1].emplace_back(i); if(bel[r]-bel[l]<=1){ qi[l].emplace_back(i); qd[r+1].emplace_back(i); } else{ qi[l].emplace_back(i); qd[rb[bel[l]]+1].emplace_back(i); qi[lb[bel[r]]].emplace_back(i); qd[r+1].emplace_back(i); } } Block::init(); for(int ps=1;ps<=n;++ps){ for(int x:qd[ps]) exi[x]=0; for(int i=1;i<=tp;++i) if(exi[s[i]]) ss[++stp]=s[i]; tp=stp;stp=0; for(int i=1;i<=tp;++i) s[i]=ss[i]; for(int x:qi[ps]) exi[x]=1,s[++tp]=x; for(int x:ui[ps]) Block::inc(x); for(int x:ud[ps]) Block::dec(x); for(int i=1;i<=tp;++i){ int x=s[i]; int cur=Block::jump(Block::qry(x)+a[ps]); if(cur>res[x]) res[x]=cur-1; } } for(int i=0;i<N;++i){ int mx=0; for(int x:vec[i]){ int l=x,r=res[x]; if(mx>=l) l=mx+1; if(l<=r){++ans[l];--ans[r+1];} if(r>mx) mx=r; } } for(int i=1;i<=m;++i) ans[i]+=ans[i-1]; for(int i=1;i<=m;++i) printf("%d\n",ans[i]); return 0; }但是这个题似乎并不能规约到一些经典根号题,所以说我们可以去思考一下 咋做。我们分块做法散块的根号平衡看起来做得很不优美,所以我们考虑只将整块的做法扩展到 。我们改为用线段树划分整个区间,每个询问在 个节点处被处理,每个节点处理的询问答案同样具有单调性。我们依旧考虑在每个线段树节点上双指针。
但是会影响到某个线段树节点的修改看起来很多?考虑我们刚才分块是怎么优化的:注意绝大部分修改都是整块修改,于是直接打标记。线段树同样有这个性质,除开完全包含某个区间的修改,剩下的与该区间有交的修改的量级是 的。考虑线段树每次区间操作只会访问 个节点所以这个性质是显然的。
完全包含某个区间的修改的贡献我们可以拿个数据结构(比如树状数组)维护出每次修改的时间。我们发现我们只需要直接从父亲继承这些修改,每次继承之后的变化量之和是 的,所以我们需要按 dfs 序处理这些区间并且动态维护这个树状数组就行了。
剩下的就和分块差不多,直接对与该区间有交的修改双指针。整块修改的贡献就从树状数组里查一下。求答案时就在树状数组上倍增一下。时间复杂度 。在本题的数据范围下不如单根号快。
#include <cstdio> #include <vector> #include <algorithm> #define lc (p<<1) #define rc (p<<1|1) using namespace std; int read(){ char c=getchar();int x=0; while(c<48||c>57) c=getchar(); do x=(x<<1)+(x<<3)+(c^48),c=getchar(); while(c>=48&&c<=57); return x; } const int N=100003; int n,m; int ql[N],qr[N],res[N]; vector<int> vec[N]; int ans[N],a[N]; vector<int> qry[N<<2],upd[N<<2]; void hang(int x,int p=1,int l=1,int r=n){ if(ql[x]<=l&&r<=qr[x]){ qry[p].emplace_back(x); return; } upd[p].emplace_back(x); int mid=(l+r)>>1; if(ql[x]<=mid) hang(x,lc,l,mid); if(qr[x]>mid) hang(x,rc,mid+1,r); } int mn[N<<2],tg[N<<2]; inline void proc(int p,int v){mn[p]+=v;tg[p]+=v;} inline void pushdown(int p){ if(tg[p]){ proc(lc,tg[p]); proc(rc,tg[p]); tg[p]=0; } } void build(int p,int l,int r){ tg[p]=0; if(l==r){mn[p]=-a[l];return;} int mid=(l+r)>>1; build(lc,l,mid); build(rc,mid+1,r); mn[p]=min(mn[lc],mn[rc]); } void update(int x,int v,int p,int l,int r){ if(ql[x]<=l&&r<=qr[x]) return proc(p,v); pushdown(p); int mid=(l+r)>>1; if(ql[x]<=mid) update(x,v,lc,l,mid); if(qr[x]>mid) update(x,v,rc,mid+1,r); mn[p]=min(mn[lc],mn[rc]); } int tr[N]; inline void mdf(int x,int v){for(int i=x;i<=m;i+=(i&-i)) tr[i]+=v;} inline int ask(int x){ int res=0; for(int i=x;i;i^=(i&-i)) res+=tr[i]; return res; } inline int jump(int v){ if(!v) return 0; int x=0; for(int i=16;~i;--i) if(x+(1<<i)<=m&&tr[x+(1<<i)]<v) v-=tr[x+=(1<<i)]; return x+1; } void dfs(int p=1,int l=1,int r=n){ build(p,l,r); for(int x:qry[p]) mdf(x,1); int len=upd[p].size(),t=0,o=0; for(int x:qry[p]){ int cur=ask(x); while(o<len&&upd[p][o]<x) update(upd[p][o++],-1,p,l,r); while(t<len&&ask(upd[p][t])-cur+mn[p]<0) update(upd[p][t++],1,p,l,r); int pos=jump(cur-mn[p])-1; if(t&&pos<upd[p][t-1]) pos=upd[p][t-1]-1; if(pos>res[x]) res[x]=pos; } if(l<r){ int mid=(l+r)>>1; dfs(lc,l,mid); dfs(rc,mid+1,r); } for(int x:qry[p]) mdf(x,-1); } int main(){ n=read();m=read(); for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=m;++i){ ql[i]=read();qr[i]=read(); vec[read()].emplace_back(i); res[i]=i;hang(i); } dfs(); for(int i=0;i<N;++i){ int mx=0; for(int x:vec[i]){ int l=x,r=res[x]; if(mx>=l) l=mx+1; if(l<=r){++ans[l];--ans[r+1];} if(r>mx) mx=r; } } for(int i=1;i<=m;++i) ans[i]+=ans[i-1]; for(int i=1;i<=m;++i) printf("%d\n",ans[i]); return 0; }
- 1
信息
- ID
- 8324
- 时间
- 5000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者