【总结】单调队列

[CEOI2020]花式围栏

题意
在所给的围栏中算出所含矩形的个数。

solution:
对于计算一个长为 l l l,宽为 r r r的矩形,可以算出方案为:
( l + 1 2 ) ∗ ( r + 1 2 ) {l+1 \choose 2}*{r+1 \choose 2} (2l+1)(2r+1)

考虑将围栏分割成若干个规则的矩形,分别计算贡献并相加。

如图,可以将围栏分成若干个由红色矩形组成的区域,相加即可。(注意,这里是求左端点在给定矩形内时的矩形数,而且必须是极大矩阵,即左右都不能扩展)

如右图,第一个的方案为:
( l + 1 2 ) ∗ ( r + 1 2 ) − ( l + 1 2 ) ∗ ( k + 1 2 ) {l+1 \choose 2}*{r+1 \choose 2}-{l+1 \choose 2}*{k+1 \choose 2} (2l+1)(2r+1)(2l+1)(2k+1)

这启示我们用单调队列来维护一个高度递增的序列。

若大于当前高度,则如队列;否则将高度大于的弹出,高度差为 m i n ( h [ t o p ] − h [ t o p − 1 ] , h [ t o p ] − h [ i ] ) min(h[top]-h[top-1],h[top]-h[i]) min(h[top]h[top1],h[top]h[i])

注意,对于高度相等的部分应该合并,而不是单独计算答案。
在这里插入图片描述

#include<bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5; const LL mod=1e9+7; LL n,a[N],b[N],ans; LL tp,h[N],w[N]; void read(LL &x) { LL f=1;x=0;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();} x*=f; } LL calc(LL x, LL y) { x %= mod; y %= mod; x = ((1 + x) * x % mod) * 500000004 % mod;//500000004是2关于膜1e9+7的乘法逆元 y = ((1 + y) * y % mod) * 500000004 % mod; return x * y % mod; } int main() { read(n); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=n;i++) read(b[i]); for(int i=1;i<=n;i++) { LL tot=0; while(tp>0&&h[tp]>a[i]) { tot+=w[tp]; ans=(ans+calc(h[tp],tot)-calc(max(h[tp-1],a[i]),tot)+mod)%mod; b[i]+=w[tp]; tp--; } h[++tp]=a[i],w[tp]=b[i]; } LL tot=0; for(int i=tp;i>=1;i--) { tot+=w[i]; ans=(ans+calc(h[i],tot)-calc(h[i-1],tot)+mod)%mod; } printf("%lld\n",ans); }

「NOIP2017普及组」跳房子

solution:
d p dp dp+二分的做法不难想到。

本题显然限制了上界和下界。也就是说,距离太小的不能进,距离太大的要舍弃。而距离是单调递增的,所以用 c n t cnt cnt记录一下位置,把符合条件的加入队列即可。不过本题不是每遍历了一个点就加入一个点,而是在移动 c n t cnt cnt指针时加入。

#include<cstdio> #include<stack> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int MAXN=500005; struct node{ ll x,w; }a[MAXN]; ll n,d,k,sum,dp[MAXN]; bool check(ll mid) { deque<ll> q; ll k1=max(1ll,d-mid),k2=d+mid; ll cnt=0; for(int i=1;i<=n;i++) { for(;cnt<i&&a[i].x-a[cnt].x>=k1;cnt++) { if(!q.size()) q.push_back(cnt); else { while(q.size()&&dp[q.back()]<=dp[cnt]) q.pop_back(); q.push_back(cnt); } } while(q.size()&&a[i].x-a[q.front()].x>k2) q.pop_front(); if(q.size()) dp[i]=dp[q.front()]+a[i].w; else dp[i]=-1e18; if(dp[i]>=k) return 1; } return 0; } int main() { scanf("%lld%lld%lld",&n,&d,&k); for(int i=1;i<=n;i++) { scanf("%lld%lld",&a[i].x,&a[i].w); if(a[i].w>0) sum+=a[i].w; } if(sum<k) { printf("-1"); return 0; } ll l=1,r=max(d-1,a[n].x-d); while(l<r) { ll mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid+1; } printf("%lld",l); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530383.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示