bzoj 2658

首先考虑容斥

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

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

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

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

画个图理解一下:

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

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

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

因此扫到每一个$a$,答案就变成了$\frac{c(c+1)}{2}a-\sum_{i=1}^{c}\sum_{j=i}^{c}max(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 @ 2019-06-25 16:56  lleozhang  Views(250)  Comments(0Edit  收藏  举报
levels of contents