【ZJOI2012】小蓝的好友(mrx)

Description

终于到达了这次选拔赛的最后一题,想必你已经厌倦了小蓝和小白的故事,为了回馈各位比赛选手,此题的主角是贯穿这次比赛的关键人物——小蓝的好友。
在帮小蓝确定了旅游路线后,小蓝的好友也不会浪费这个难得的暑假。与小蓝不同,小蓝的好友并不想将时间花在旅游上,而是盯上了最近发行的即时战略游戏——SangoCraft。但在前往通关之路的道路上,一个小游戏挡住了小蓝的好友的步伐。
“国家的战争其本质是抢夺资源的战争”是整款游戏的核心理念,这个小游戏也不例外。简单来说,用户需要在给定的长方形土地上选出一块子矩形,而系统随机生成了N个资源点,位于用户所选的长方形土地上的资源点越多,给予用户的奖励也越多。悲剧的是,小蓝的好友虽然拥有着极其优秀的能力,但同时也有着极差的RP,小蓝的好友所选的区域总是没有一个资源点。
终于有一天,小蓝的好友决定投诉这款游戏的制造厂商,为了搜集证据,小蓝的好友想算出至少包含一个资源点的区域的数量。作为小蓝的好友,这自然是你分内之事。

Solution

笛卡尔树

开始还以为这是单调栈什么的,但是好大啊……
想到单调栈有些其实可以用笛卡尔树来实现。

先转化思路

很容易想到用矩阵数的总数减去空白矩阵的总数。
假如一个矩阵的长为a和b,那么含有的矩阵数为 C2a+1C2b+1=(a+1)a(b+1)b4

想一想扫描线

从下往上开始扫描,那么我们只要存储每个点最大向上的距离和涵盖的最大矩阵的宽度就可以了。

如何找这一个矩阵

首先我们建立一棵笛卡尔树(我这里用treap来打,因为treap的最小堆性质让它是一个笛卡尔树),每个节点存储key(这个节点最大向上的高度),存储size(这个节点向左右的最大宽度,因为用笛卡尔树来存储,那么size就是子节点的个数),存储sum(这个节点的子节点的贡献总和,不包括当前的这个节点)。
很显然每次碰到一个黑点,就把在树中的这个节点的key清0,然后再把它旋转到根节点,那么它在高度为0时就可以涵盖所有的节点。
那么这个节点的父节点之间的答案贡献就是 size[x](size[x]+1)(key[x]key[f[x]])2 ,因为在笛卡尔树中如果点可以涵盖子节点,那么答案就很明显了。
每次再给root的key+1并打个懒标记就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=100007;
typedef long long ll;
int i,j,k,l,n,m,c;
int first[maxn],last[maxn],next[maxn],num,root,t[maxn][2];
int size[maxn];
ll key[maxn],sum[maxn],add[maxn],ans;
void add1(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
ll suan(int x){return x*(x+1)/2;}
void back(int x,int y){
    key[x]+=y;add[x]+=y;
}
void update(int x){
    size[x]=1;sum[x]=0;
    if(t[x][0]){
        size[x]+=size[t[x][0]];
        sum[x]+=sum[t[x][0]]+suan(size[t[x][0]])*(key[t[x][0]]-key[x]);
    }
    if(t[x][1]){
        size[x]+=size[t[x][1]];
        sum[x]+=sum[t[x][1]]+suan(size[t[x][1]])*(key[t[x][1]]-key[x]);
    }
}
void build(int &x,int l,int r){
    if(l>r){x=0;return;}
    int mid=(l+r)/2;
    x=mid;
    build(t[x][0],l,mid-1);
    build(t[x][1],mid+1,r);
    update(x);
}
void remove(int x){
    if(add[x]){
        if(t[x][0])back(t[x][0],add[x]);
        if(t[x][1])back(t[x][1],add[x]);
        update(x);
        add[x]=0;
    }
}
void rotate(int &x,int z){
    int y=t[x][z];
    t[x][z]=t[y][1-z];
    t[y][1-z]=x;
    update(x);update(y);
    x=y;
}
void change(int &x,int y){
    remove(x);
    if(y==size[t[x][0]]+1){
        key[x]=0;
        update(x);
        return;
    }
    if(y0]]+1){
        change(t[x][0],y);
        rotate(x,0);
    }
    else{
        change(t[x][1],y-size[t[x][0]]-1);
        rotate(x,1);

    }
}
int main(){
    scanf("%d%d%d",&n,&m,&c);
    fo(i,1,c){
        scanf("%d%d",&k,&l);
        add1(k,l);
    }
    ans=suan(n)*suan(m);
    build(root,1,m);
    fo(j,1,n){
        back(root,1);
        rep(i,j){
            change(root,last[i]);
        }
        ans-=sum[root]+suan(size[root])*key[root];
    }
    printf("%lld\n",ans);
}

你可能感兴趣的:(省选,treap,笛卡尔树)