bzoj 2658

首先考虑容斥

我们计算出所有没有点在其中的矩形,然后用所有矩形减去这些矩形即可

然后考虑如何计算没有点在其中的矩形

采用扫描线的思想,从上向下一行一行扫,假设我们扫到的行编号是a,然后考虑如果左右的列端点是[l,r],那么这一行向上至多能扩展几个矩形呢?

显然,我们要找到区间[l,r]中位置最下面的那个点,设其行编号为w[i],那么矩形数量即为aw[i]

画个图理解一下:

很清楚的就能看到,现在扫到的是红色的a,左右区间是蓝色的[l,r],那么上界会被限制在w[i]这条黄线处,能向上延伸的矩形数量也就是aw[i]

由于a是定值,因此我们考虑每个w[i]会对多少个区间[l,r]产生贡献

显然,w[i]必须是区间[l,r]中的最大值!

因此扫到每一个a,答案就变成了c(c+1)2ai=1cj=icmax(i,j),其中max(i,j)表示区间[i,j]中最大的w

这个看着可以用单调栈维护维护嘛...

可是我们每次向下扫描的时候,w都会改变!

因此我们需要一个能够支持修改的数据结构,显然是一种二叉树

这个二叉树需要支持修改,最好能保证是一个大根堆,而且还要保证中序遍历得到的是原序列

这个...有点难?

treap嘛!

把序列的下标扔进二叉搜索树里,再把w作为权值体现堆的性质就可以了嘛

(其实就是把原来随机的一个权值变成了一个w

由于数据随机,所以可以通过

这样每个点的贡献就是(siz[lson]+1)(siz[rson]+1)w

注意在修改时如果先删除再重新插入会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;
}
复制代码

 

posted @   lleozhang  Views(266)  Comments(0Edit  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· 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
levels of contents
点击右上角即可分享
微信分享提示