bzoj 2658
首先考虑容斥
我们计算出所有没有点在其中的矩形,然后用所有矩形减去这些矩形即可
然后考虑如何计算没有点在其中的矩形
采用扫描线的思想,从上向下一行一行扫,假设我们扫到的行编号是,然后考虑如果左右的列端点是,那么这一行向上至多能扩展几个矩形呢?
显然,我们要找到区间中位置最下面的那个点,设其行编号为,那么矩形数量即为
画个图理解一下:
很清楚的就能看到,现在扫到的是红色的,左右区间是蓝色的,那么上界会被限制在这条黄线处,能向上延伸的矩形数量也就是
由于是定值,因此我们考虑每个会对多少个区间产生贡献
显然,必须是区间中的最大值!
因此扫到每一个,答案就变成了,其中表示区间中最大的
这个看着可以用单调栈维护维护嘛...
可是我们每次向下扫描的时候,都会改变!
因此我们需要一个能够支持修改的数据结构,显然是一种二叉树
这个二叉树需要支持修改,最好能保证是一个大根堆,而且还要保证中序遍历得到的是原序列
这个...有点难?
treap嘛!
把序列的下标扔进二叉搜索树里,再把作为权值体现堆的性质就可以了嘛
(其实就是把原来随机的一个权值变成了一个)
由于数据随机,所以可以通过
这样每个点的贡献就是
注意在修改时如果先删除再重新插入会T,考虑到每次修改权值只增不减,因此每个节点只会向上转,因此我们直接修改即可
代码:
#include <cstdio> #include <algorithm> #define ll unsigned long long #define ls tree[rt].lson #define rs tree[rt].rson using namespace std; struct Treap { int lson,rson; int size,val; int rank; ll sum; }tree[300005]; struct POS { int x,y; friend bool operator < (POS a,POS b) { return a.x<b.x; } }p[100005]; int tot=0; int rot; ll sum=0; int r,c,n; inline void update(const int &rt) { tree[rt].size=tree[ls].size+tree[rs].size+1; tree[rt].sum=1ll*(tree[ls].size+1)*1ll*(tree[rs].size+1)*tree[rt].rank+tree[ls].sum+tree[rs].sum; } inline void lturn(int &rt) { int temp=rs; rs=tree[temp].lson; tree[temp].lson=rt; tree[temp].size=tree[rt].size; update(rt); rt=temp; } inline void rturn(int &rt) { int temp=ls; ls=tree[temp].rson; tree[temp].rson=rt; tree[temp].size=tree[rt].size; update(rt); rt=temp; } void buildtree(int &rt,int l,int r) { rt=++tot; if(l==r){tree[rt].val=l,tree[rt].size=1;return;} int mid=(l+r)>>1; tree[rt].val=mid; if(l<mid)buildtree(ls,l,mid-1); if(r>mid)buildtree(rs,mid+1,r); update(rt); } void ins(int &rt,int v,int w) { if(!rt)return; if(tree[rt].val==v) { tree[rt].rank=w; update(rt); return; } if(v<tree[rt].val) { ins(ls,v,w); if(tree[ls].rank>tree[rt].rank)rturn(rt); }else { ins(rs,v,w); if(tree[rs].rank>tree[rt].rank)lturn(rt); } update(rt); } inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { r=read(),c=read(),n=read(); for(register int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(); buildtree(rot,1,c); ll ans=0; sort(p+1,p+n+1); int las=1; for(register int i=1;i<=r;++i)//枚举每一行 { while(p[las].x==i&&las<=n)ins(rot,p[las].y,i),las++; ans+=c*(c+1)/2*1ll*i-tree[rot].sum; } printf("%llu\n",c*(c+1)/2ll*1ll*r*(r+1)/2ll-ans); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY