zoukankan      html  css  js  c++  java
  • 【BZOJ2658】[Zjoi2012]小蓝的好友(mrx) 平衡树维护笛卡尔树+扫描线

    【BZOJ2658】[Zjoi2012]小蓝的好友(mrx)

    Description

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

    Input

     每个输入文件中仅包含一个测试数据。
    第一行包含两个由空格隔开的正整数R,C,N,表示游戏在一块[1,R]X[1,C]的地图上生成了N个资源点。
         接下来有N行,每行包含两个整数 x,y,表示这个资源点的坐标
    (1<=x<=R,1<=Y<=c)。

    Output

          输出文件应仅包含一个整数,表示至少包含一个资源点的区域的数量。具体的说,设N个资源点的坐标为(i=1..n),你需要计算有多少个四元组(LB,DB,RB,UB)满足1<=LB<=RB<=R,1<=DB<=UB<=C,且存在一个i使得LB<=Xi<=RB,DB<=Yi<=UB均成立

    Sample Input

    5 5 4
    1 2
    2 3
    3 5
    4 1

    Sample Output

    139

    HINT

    【数据范围】
    对于100%的数据,R,C<=40000,N<=100000,资源点的位置两两不同,且位置为随机生成。

    题解:第一思路一定是补集转化,我们改求不包含黑点的矩形个数。然后考虑枚举矩形的底边所在行,考虑这一行的贡献是什么。

    我们将这一行中,每一列上面遇到的第一个黑点到这一行的距离定义为这一列的高度。然后我们对这一行中,所有列的高度建出一棵笛卡尔树。假设树上第i个节点的子树大小是siz[i],高度是h[i],那么这个点对答案的贡献就是${siz imes (siz+1) over 2} imes(h[i]-h[fa])$。

    这样做的复杂度是O(Rn)的。但是我们发现数据是随机的,随机数据有什么性质?随机序列的笛卡尔树的树高是O(logn)的,并且笛卡尔树其实是一棵Treap!所以我们可以考虑用Treap维护笛卡尔树。当我们向下平移一行时,首先所有点的高度+1,这个打标记就好;然后这一行可能冒出来一些黑点,我们将这些黑点旋转上来,然后将高度变成0即可。我们还要动态维护一下所有点的${siz imes (siz+1) over 2} imes(h[i]-h[fa])$,这个比较麻烦,需要注意一下细节。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using namespace std;
    const int maxn=100010;
    typedef long long ll;
    vector<int> px[maxn];
    vector<int>::iterator it;
    ll ans;
    int R,C,n,rt;
    struct node
    {
    	int ch[2],siz,fa,h,tag;
    	ll sum;
    }s[maxn];
    inline ll c(const ll &x) {return x*(x+1)>>1;}
    inline void add(int x,int y) {s[x].h+=y,s[x].tag+=y;}
    inline void pushdown(int x)
    {
    	if(s[x].tag)
    	{
    		if(s[x].ch[0])	add(s[x].ch[0],s[x].tag);
    		if(s[x].ch[1])	add(s[x].ch[1],s[x].tag);
    		s[x].tag=0;
    	}
    }
    inline void pushup(int x)
    {
    	s[x].siz=s[s[x].ch[0]].siz+s[s[x].ch[1]].siz+1;
    	s[x].sum=s[s[x].ch[0]].sum+s[s[x].ch[1]].sum+c(s[x].siz)*(s[x].h-s[s[x].fa].h);
    }
    void updata(int x)
    {
    	if(x!=rt)	updata(s[x].fa);
    	pushdown(x);
    }
    inline void rotate(int x)
    {
    	int y=s[x].fa,z=s[y].fa,d=(x==s[y].ch[1]);
    	if(y==rt)	rt=x;
    	else	s[z].ch[y==s[z].ch[1]]=x;
    	s[x].fa=z,s[y].fa=x,s[y].ch[d]=s[x].ch[d^1];
    	if(s[x].ch[d^1])	s[s[x].ch[d^1]].fa=y,pushdown(s[x].ch[d^1]),pushup(s[x].ch[d^1]);
    	s[x].ch[d^1]=y;
    	pushup(y),pushup(x);
    }
    int build(int l,int r)
    {
    	if(l>r)	return 0;
    	int x=(l+r)>>1;
    	s[x].ch[0]=build(l,x-1),s[x].ch[1]=build(x+1,r),s[x].siz=r-l+1;
    	if(s[x].ch[0])	s[s[x].ch[0]].fa=x;
    	if(s[x].ch[1])	s[s[x].ch[1]].fa=x;
    	return x;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	R=rd(),C=rd(),n=rd();
    	int i,a,b;
    	for(i=1;i<=n;i++)	a=rd(),b=rd(),px[b].push_back(a);
    	rt=build(1,R);
    	for(i=C;i>=1;i--)
    	{
    		add(rt,1),pushup(rt);
    		for(it=px[i].begin();it!=px[i].end();it++)
    		{
    			a=*it,updata(a);
    			while(a!=rt)	rotate(a);
    			s[a].h=0;
    			if(s[a].ch[0])	pushdown(s[a].ch[0]),pushup(s[a].ch[0]);
    			if(s[a].ch[1])	pushdown(s[a].ch[1]),pushup(s[a].ch[1]);
    			pushup(a);
    		}
    		ans+=s[rt].sum;
    	}
    	printf("%lld",c(R)*c(C)-ans);
    	return 0;
    }
  • 相关阅读:
    for循环
    while循环语句的几种方式
    IF....Else循环
    Python——文件读取
    数据结构之二叉树
    JAVA的免费天气api接口调用示例
    JAVA API
    JAVA爬虫代码
    普通网页怎么改成响应式布局
    循环遍历二叉树
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/8142697.html
Copyright © 2011-2022 走看看