【学习笔记】【集训队互测2015】上帝之手

这就是 N O I NOI NOI难度的数据结构吗。

一上来就甩了三个子问题给我。如果没有观察到一些非常重要的性质,这道题无论用多么复杂的数据结构都是难以求解的。从表面上看,这道题还套了区间查询,计数和第 K K K大的外衣,无疑是非常困难的题目。

我尝试感知这道题目。实际上这道题要问的东西 并没有那么复杂。考虑从拆分 min ⁡ \min min函数入手。式子比较繁琐,考虑慢慢分析:假设以 X i − 1 X_{i-1} Xi1为初值从 i i i出发走到 j j j s u m D i = ∑ j ≤ i D j sumD_i=\sum_{j\le i}D_j sumDi=jiDj,那么 X j = min ⁡ ( X i − 1 + s u m D j − s u m D i − 1 , l k + s u m D j − s u m D k ) X_j=\min(X_{i-1}+sumD_j-sumD_{i-1},l_k+sumD_j-sumD_k) Xj=min(Xi1+sumDjsumDi1,lk+sumDjsumDk),其中 k ∈ [ i , j ] k\in [i,j] k[i,j]。因为 j j j是固定的,所以区间查询最小值可以做到 O ( 1 ) O(1) O(1)计算。对于第一问,可以将 X i − 1 X_{i-1} Xi1替代成 l i − 1 l_{i-1} li1,这样上式取值就是单调的,可以直接计算。从另一个角度来想,第一问也是一个很好的提示,因为直接根据定义求就能得到非常有规律的式子。

考虑第二问。此时 X 0 X_0 X0为定值,直接看上式一项递增而另一项递减,二分即可,这里不再赘述。

考虑第三问。我们需要一个能支持修改的数据结构。原问题实际上等价于从右端点开始每次跳向左边第一个比它小的位置,跳过的总次数。这是一个非常经典的问题,可以用线段树在 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)时间内求解。

可能以后要习惯这样根据部分分深入的题目。做不动是另一回事,但是总之还是要下定决心去肝。

#include<bits/stdc++.h> #define ll long long #define int ll #define fi first #define se second #define pb push_back #define inf 0x3f3f3f3f using namespace std; const int N=1e5+5; int n,Q,d[N],sumd[N],limit[N]; struct node{ int min,val; }t[N<<2]; int query(int p,int l,int r,int now){ if(l==r)return t[p].min<now; if(t[p].min>=now)return 0; int mid=l+r>>1; if(t[p<<1|1].min<now){ return t[p].val-t[p<<1|1].val+query(p<<1|1,mid+1,r,now); } else{ return query(p<<1,l,mid,now); } } int querymin(int p,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr)return t[p].min; int mid=l+r>>1; if(qr<=mid)return querymin(p<<1,l,mid,ql,qr); if(mid<ql)return querymin(p<<1|1,mid+1,r,ql,qr); return min(querymin(p<<1,l,mid,ql,qr),querymin(p<<1|1,mid+1,r,ql,qr)); } void pushup(int p,int l,int r){ int mid=l+r>>1; t[p].min=min(t[p<<1].min,t[p<<1|1].min); t[p].val=t[p<<1|1].val+query(p<<1,l,mid,t[p<<1|1].min); } void build(int p,int l,int r){ if(l==r){ t[p].min=limit[l]-sumd[l]; t[p].val=1; return; } int mid=l+r>>1; build(p<<1,l,mid),build(p<<1|1,mid+1,r); pushup(p,l,r); } void update(int p,int l,int r,int x){ if(l==r){ t[p].min=limit[l]-sumd[l]; t[p].val=1; return; } int mid=l+r>>1; x<=mid?update(p<<1,l,mid,x):update(p<<1|1,mid+1,r,x); pushup(p,l,r); } int Query(int p,int l,int r,int ql,int qr,int now){ if(ql>qr)return 0; if(ql<=l&&r<=qr)return query(p,l,r,now); int mid=l+r>>1; if(qr<=mid)return Query(p<<1,l,mid,ql,qr,now); if(mid<ql)return Query(p<<1|1,mid+1,r,ql,qr,now); int tmp=querymin(p<<1|1,mid+1,r,mid+1,qr); if(tmp>=now)return Query(p<<1,l,mid,ql,mid,now); return Query(p<<1|1,mid+1,r,mid+1,qr,now)+Query(p<<1,l,mid,ql,mid,tmp); } signed main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>Q; for(int i=1;i<=n;i++)cin>>d[i],sumd[i]=sumd[i-1]+d[i]; for(int i=0;i<=n;i++)cin>>limit[i]; build(1,0,n); while(Q--){ int type; cin>>type; if(type==0){ int u,x;cin>>u>>x; //fixed limit[u]=x,update(1,0,n,u); } else if(type==1){ int l,r,K;cin>>l>>r>>K; cout<<querymin(1,0,n,r-K,r)+sumd[r]<<"\n"; } else if(type==2){ int l,r,X;cin>>l>>r>>X; int L=l,R=r,res=l; while(L<=R){ int mid=L+R>>1; if(X-sumd[mid-1]>=querymin(1,0,n,mid,r)){ res=mid,L=mid+1; } else{ R=mid-1; } } int res2=min(X-sumd[res-1],querymin(1,0,n,res,r)); if(res<r)res2=max(res2,min(X-sumd[res],querymin(1,0,n,res+1,r))); cout<<res2+sumd[r]<<"\n"; } else{ int l,r;cin>>l>>r; cout<<Query(1,0,n,l-1,r-2,min(limit[r-1]-sumd[r-1],limit[r]-sumd[r]))+1<<"\n"; } } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17529965.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(41)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-06-03 杂题选做。
2022-06-03 构造题专练
点击右上角即可分享
微信分享提示