[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}
\]
我们的笛卡尔树上的的每个节点都要维护一个这样的信息。

现在我们还需要扫描每一行,动态维护这颗笛卡尔树。

如果这行没有障碍点,我们整体加个1就好了,这个可以直接打标记。

对于障碍点,相当于这个位置的值变成了0,那么我们把这个点旋转上来就好了,通过手玩我们可以发现\(rotate\)操作不会破坏除了这个点以外的其他点的笛卡尔树结构,于是我们可以一直\(rotate\)把这个点转上去,顺便更新一下答案就好了,因为是随机的数据,所以每次期望操作次数是\(log\)的。

注意如果按照上面的\(\Delta h\)那样算贡献的话如果一个点的父亲改变了的话这个点需要重新\(pushup\)一次。

代码

#include<bits/stdc++.h>
#define N 100009
#define ls tr[x][0]
#define rs tr[x][1]
using namespace std;
typedef long long ll;
vector<int>vec[N];
vector<int>::iterator it;
int tr[N][2],fa[N],size[N],h[N],la[N],n,m,num,rot;
ll dp[N],ans;
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
inline ll calc(ll x){return x*(x+1)/2;}
inline bool ge(int x){return tr[fa[x]][1]==x;}
inline void pushup(int x){
    size[x]=size[ls]+size[rs]+1;
    dp[x]=dp[ls]+dp[rs]+1ll*(h[x]-h[fa[x]])*calc(size[x]);
}
inline void rotate(int x){
    int y=fa[x],o=ge(x);
    tr[y][o]=tr[x][o^1];fa[tr[y][o]]=y;
    if(fa[y])tr[fa[y]][ge(y)]=x;fa[x]=fa[y];
    fa[y]=x;tr[x][o^1]=y;
    if(tr[y][o])pushup(tr[y][o]);pushup(y);pushup(x);
}
inline void add(int x,int y){
    h[x]+=y;la[x]+=y;
    pushup(x);
}
inline void pushdown(int x){
    if(la[x]){
        if(ls)add(ls,la[x]);
        if(rs)add(rs,la[x]);
        la[x]=0;
    }
}
void _pushdown(int x){
    if(fa[x])_pushdown(fa[x]);
    pushdown(x);
}
int build(int l,int r){
    if(l>r)return 0;
    int x=(l+r)>>1;
    ls=build(l,x-1);rs=build(x+1,r);
    if(ls)fa[ls]=x;if(rs)fa[rs]=x;
    size[x]=size[ls]+size[rs]+1;
    return x;
}
void dfs(int x){
    pushdown(x);
    if(ls)dfs(ls);
    cout<<x<<" "<<ls<<" "<<rs<<" "<<h[ls]<<" "<<h[rs]<<" "<<h[x]<<" "<<dp[x]<<endl;
    if(rs)dfs(rs);
}
int main(){
    n=rd();m=rd();num=rd();
    rot=build(1,m);
    for(int i=1;i<=num;++i){
        int x,y;
        x=rd(),y=rd();
        vec[x].push_back(y);
    }
    for(int i=1;i<=n;++i){
        add(rot,1);
        for(it=vec[i].begin();it!=vec[i].end();++it){
            int x=*it;
            _pushdown(x);
            while(fa[x])rotate(x);
            h[x]=0;
            if(ls)pushup(ls);if(rs)pushup(rs);
            pushup(x);
            rot=x;
        }
        ans+=dp[rot];
    }
    printf("%lld",calc(n)*calc(m)-ans);
    return 0;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/10798060.html

时间: 2024-11-10 01:02:04

[ZJOI2012]小蓝的好友的相关文章

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】小蓝的好友 - 题解

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

BZOJ 2658 小蓝的好友

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2658 题意:给出一个n*m的格子.某些格子中有障碍.求包含至少一个障碍的矩形有多少个? 思路:我们求空白矩形的个数. 从上到下一行一行计算,每到达一行,计算以该行为底的空白矩形个数.我们只需要知道每列向上延伸的最大距离. 这个可以看做是一棵树 我们只需要记录每个的高度即可. 那么每次增加一行,若整行都没有障碍,则根节点的高度增加1.否则,障碍将树分为若干子树.这个操作可以用fhq

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

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

【BZOJ2656】 [Zjoi2012]数列(sequence)

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

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

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

BZOJ-2657: [Zjoi2012]旅游(journey) (树形DP求最长链)

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

【BZOJ2656】[Zjoi2012]数列(sequence) 高精度

[BZOJ2656][Zjoi2012]数列(sequence) Description 小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式: 小白作为一个数学爱好者,很快就计算出了这个数列的通项公式.于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来.于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:即让小蓝说一个正整数N,小白则说出 的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了

(树形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边