洛谷P2611 [ZJOI2012]小蓝的好友

总子矩形个数减去内部没有资源点的子矩形个数即为答案。

考虑枚举子矩形的下边界,从上到下进行扫描。对列建笛卡尔树,其中序遍历为下标,维护堆性质的权值为该列上点的行数最大值。然后在笛卡尔树上的每个节点统计贡献。

扫描时需要支持单点修改权值,那么直接用 (FHQ Treap) 建笛卡尔树即可,因为数据随机,所以复杂度有保证。

#include<bits/stdc++.h>
#define maxn 200010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,m,k,tot,root;
int ls[maxn],rs[maxn],val[maxn],siz[maxn];
ll ans;
ll key[maxn],sum[maxn];
vector<int> ve[maxn];
void pushup(int x)
{
    siz[x]=siz[ls[x]]+siz[rs[x]]+1;
    sum[x]=sum[ls[x]]+sum[rs[x]]+key[x]*(siz[ls[x]]+1)*(siz[rs[x]]+1);
}
void build(int l,int r,int &x)
{
    int mid=(l+r)>>1;
    x=++tot,siz[x]=1,val[x]=mid;
    if(l<mid) build(l,mid-1,ls[x]);
    if(r>mid) build(mid+1,r,rs[x]);
    pushup(x);
}
void merge(int &p,int x,int y)
{
    if(!x||!y)
    {   
        p=x+y;
        return;
    }
    if(key[x]>key[y]) p=x,merge(rs[p],rs[p],y);
    else p=y,merge(ls[p],x,ls[p]);
    pushup(p);
}
void split(int p,int k,int &x,int &y)
{
    if(!p)
    {
        x=y=0;
        return;
    }
    if(val[p]<=k) x=p,split(rs[p],k,rs[p],y);
    else y=p,split(ls[p],k,x,ls[p]);
    pushup(p);
}
void modify(int k,int v)
{
    int x,y,z;
    split(root,k,x,y),split(x,k-1,x,z);
    key[z]=v,pushup(z),merge(x,x,z),merge(root,x,y);
}
int main()
{
    read(n),read(m),read(k),build(1,m,root);
    for(int i=1;i<=k;++i)
    {
        int x,y;
        read(x),read(y),ve[x].push_back(y);
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<ve[i].size();++j) modify(ve[i][j],i);
        ans+=(ll)i*m*(m+1)/2-sum[root];
    }
    printf("%lld",(ll)n*(n+1)/2*m*(m+1)/2-ans);
    return 0;
}
原文地址:https://www.cnblogs.com/lhm-/p/13907923.html