BZOJ 2658 小蓝的好友

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2658

题意:给出一个n*m的格子。某些格子中有障碍。求包含至少一个障碍的矩形有多少个?

思路:我们求空白矩形的个数。

从上到下一行一行计算,每到达一行,计算以该行为底的空白矩形个数。我们只需要知道每列向上延伸的最大距离。

这个可以看做是一棵树

我们只需要记录每个的高度即可。

那么每次增加一行,若整行都没有障碍,则根节点的高度增加1.否则,障碍将树分为若干子树。这个操作可以用fhq treap实现,即通过树的分离和合并实现所有操作。

合并两个子树AB时,要求A的所有元素小于B。或者将A设为B的左孩子,或者将B设为A的右孩子。这个操作比较简单。

对于分离操作,用split(now,x,y,K)表示,将now为根的子树的前K个孩子分离,分开后的两个子树的根分别是x,y

设K=5

分离红色圈住的5个节点,过程如下:

(这个图来自http://hi.baidu.com/wdxertqdtscnwze/item/7b6a9419be7c68cd756a8498)

const int N=100005;

struct node
{
    int c[2],h,det,size;
    i64 ans;

    void add(int x)
    {
        h+=x;
        det+=x;
    }
};

node a[N];
int root;

#define C2(x) ((x)*((x)+1)/2)

void pushUp(int t)
{
    if(!t) return;
    a[t].size=1;
    a[t].ans=0;
    for(int i=0;i<2;i++) if(a[t].c[i])
    {
        int p=a[t].c[i];
        a[t].size+=a[p].size;
        a[t].ans+=a[p].ans;
        a[t].ans+=(i64)(a[p].h-a[t].h)*C2(a[p].size);
    }
}

void pushDown(int t)
{
    if(!t||!a[t].det) return;
    if(a[t].c[0]) a[a[t].c[0]].add(a[t].det);
    if(a[t].c[1]) a[a[t].c[1]].add(a[t].det);
    a[t].det=0;
}

pair<int,int> split(int u,int k)
{
    if(!u) return MP(0,0);
    pushDown(u);
    if(a[a[u].c[0]].size+1<=k)
    {
        k-=a[a[u].c[0]].size+1;
        pair<int,int> tmp=split(a[u].c[1],k);
        a[u].c[1]=tmp.first;
        pushUp(u);
        return MP(u,tmp.second);
    }
    else
    {
        pair<int,int> tmp=split(a[u].c[0],k);
        a[u].c[0]=tmp.second;
        pushUp(u);
        return MP(tmp.first,u);
    }
}

int merge(int u,int v)
{
    if(!u||!v) return u+v;
    pushDown(u);
    pushDown(v);
    if(a[u].h<a[v].h)
    {
        a[u].c[1]=merge(a[u].c[1],v);
        pushUp(u);
        return u;
    }
    else
    {
        a[v].c[0]=merge(u,a[v].c[0]);
        pushUp(v);
        return v;
    }
}

pair<int,int> b[N];
int n,m,K;

int main()
{

    n=myInt();
    m=myInt();
    K=myInt();
    for(int i=1;i<=K;i++)
    {
        b[i].first=myInt();
        b[i].second=myInt();
    }
    sort(b+1,b+K+1);
    for(int i=1;i<=m;i++)
    {
        root=merge(root,i);
        pushUp(root);
    }
    int cur=1;
    i64 ans=(i64)C2(n)*C2(m);

    for(int i=1;i<=n;i++)
    {
        a[root].add(1);
        while(cur<=K&&b[cur].first==i)
        {
            pair<int,int> tmp1=split(root,b[cur].second-1);
            pair<int,int> tmp2=split(tmp1.second,1);
            a[tmp2.first].h=0;
            root=merge(tmp1.first,tmp2.first);
            root=merge(root,tmp2.second);
            cur++;
        }
        ans-=a[root].ans;
        ans-=(i64)a[root].h*C2(a[root].size);
    }
    printf("%lld\n",ans);
}
时间: 2025-01-16 05:00:48

BZOJ 2658 小蓝的好友的相关文章

【ZJOI2012】小蓝的好友 - 题解

题目链接 [ZJOI2012]小蓝的好友 做法 至少包含一个点的矩阵数等于总矩阵数减去不包含任意一个点的矩阵数. 考虑将点按照纵坐标排序,进行类似扫描线的操作.每一列有用的点是最靠近当前行的点,记录它们的纵坐标.如果这一层存在一段长度为 $ s $ 不包含点,则它对答案的贡献为 $ \frac{(s + 1) \times s}{2} $ :如果是纵坐标上一段都满足,那就再乘上纵坐标上的长度. 维护一个 $ Treap $ ,$ value $ 值中序遍历表示横坐标的一段,$ key $ 值表示

bzoj2658: [Zjoi2012]小蓝的好友(mrx)

太神辣 treap的随机键值竟然能派上用场.. 要用不旋转的treap来进行维护区间信息 1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<iostream> 6 7 using namespace std; 8 9 template<typename Q> Q &read(Q &x)

[ZJOI2012]小蓝的好友

https://www.luogu.org/problemnew/show/P2611 题解 \(n\times m\)肯定过不去.. 我们把给定的点看做障碍点,考虑先补集转化为求全空矩阵. 然后我们枚举每一行,令这一行每个点的权值为从这点向上的极大不包含障碍点的连续段. 然后对这个序列建立笛卡尔树,那么答案为: \[ f[x]=(h[x]-h[fa[x]])*\frac{szie[x]*(size[x]+1)}{2} \] 我们的笛卡尔树上的的每个节点都要维护一个这样的信息. 现在我们还需要扫

(树形DP) bzoj 2657

2657: [Zjoi2012]旅游(journey) Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 604  Solved: 387[Submit][Status][Discuss] Description 到了难得的暑假,为了庆祝小白在数学考试中取得的优异成绩,小蓝决定带小白出去旅游~~ 经过一番抉择,两人决定将T国作为他们的目的地.T国的国土可以用一个凸N边形来表示,N个顶点表示N个入境/出境口.T国包含N-2个城市,每个城市都是顶点均为N边

【BZOJ 2656】2656: [Zjoi2012]数列(sequence) (高精度)

2656: [Zjoi2012]数列(sequence) Time Limit: 2 Sec  Memory Limit: 128 MBSubmit: 1499  Solved: 786 Description 小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式: 小白作为一个数学爱好者,很快就计算出了这个数列的通项公式.于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来.于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了

bzoj 2656 [Zjoi2012]数列(sequence) 递推+高精度

2656: [Zjoi2012]数列(sequence) Time Limit: 2 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式: 小白作为一个数学爱好者,很快就计算出了这个数列的通项公式.于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来.于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了

【BZOJ2229】【ZJOI2011】最小割

冷门知识点-- 原题: 小白在图论课上学到了一个新的概念--最小割,下课后小白在笔记本上写下了如下这段话: "对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割" 现给定一张无向图,小白有若干个形如"图中有多少对点它们的最小割的容量不超过x呢"的疑问,小蓝虽然很

【BZOJ-2229】最小割 最小割树(最大流+分治)

2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status][Discuss] Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的

【BZOJ2656】 [Zjoi2012]数列(sequence)

2656: [Zjoi2012]数列(sequence) Time Limit: 2 Sec  Memory Limit: 128 MBSubmit: 1485  Solved: 779[Submit][Status][Discuss] Description 小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式: 小白作为一个数学爱好者,很快就计算出了这个数列的通项公式.于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来.于是小白为了向小蓝