• [ZJOI2012][BZOJ2658]小蓝的好友(Treap维护笛卡尔树)


    题面

    https://darkbzoj.tk/problem/2658

    题解

    前置知识

    看见“至少包含一个”这种字眼,多少感觉正向做会比取补要来得麻烦一些,于是取补,从矩形总数(frac{R(R+1)}{2} imesfrac{C(C+1)}{2})中减去那些不包含任何点的。

    在统计不包含任何点的矩形的个数时,先从上到下枚举矩形的下边所在直线。(不妨设x轴正方向向右,y轴正方向向下)设当前直线为(y=cury),对横坐标x=1~R,定义h[x]为所有横坐标为x,纵坐标(leq cury)的点中,纵坐标最大者的纵坐标。那么所有下边在(y=cury)上的矩形的总数就是

    [{sumlimits_{i{leq}j}}minlimits_{k=i}^{j}h[k]-cury ]

    [=cury imes frac{R(R+1)}{2} - sumlimits_{i{leq}j}minlimits_{k=i}^{j}h[k] ]

    而第二项可以转化

    [{sumlimits_{i{leq}j}}minlimits_{k=i}^{j}h[k] ]

    对h数组建出笛卡尔树后,分别对树上每一个节点u考虑贡献:h[u]对点对(i,j)有贡献,当且仅当(i{leq}u{leq}j)且i,j都在u的子树内。所以u此时的总贡献是(h[u]*(sz[c[u][0]]+1)(sz[c[u][1]]+1))

    现在就变成了一道纯数据结构题。要求在笛卡尔树上,维护一个序列h,每次可以单点修改,或者查询总体的(h[u]*(sz[c[u][0]]+1)(sz[c[u][1]]+1))的和。

    但是单靠笛卡尔树无法修改啊?并不是,其实笛卡尔树本身“兼容”插入删除等操作,因为它的结构和Treap一模一样,Treap中的权值对应笛卡尔树中的key(本题中key是行编号),优先级对应val(本题中是h)。Treap中的insert,remove函数(或者FHQ Treap中的split,merge)笛卡尔树同样能用。

    怎么修改呢?相当于把某一个点拿出来并从树中删掉,修改它的值,再重新加进树里去。如果用FHQ Treap的话,就相当于把某一个点和它前后全部拆开,修改它的值,再和它前后全部merge起来。

    总时间复杂度(O(n log n + C log R))

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define rg register
    #define In inline
    #define ll long long
    
    const int L = 40000;
    const int N = 100000;
    
    typedef pair<ll,ll>pll;
    
    In ll read(){
    	ll s = 0,ww = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
    	while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
    	return s * ww;
    }
    
    int n;
    ll R,C;
    
    struct CartTree{
    	int cnt,rt;
    	int c[N+5][2],val[N+5],pri[N+5]; //val即第几列,pri即h
    	ll sz[N+5],sum[N+5];
    	int create(int x){
    		cnt++;
    		val[cnt] = x;
    		pri[cnt] = 0; //初始h都为0
    		sz[cnt] = 1;
    		return cnt;
    	}
    	void pushup(int u){
    		int lc = c[u][0],rc = c[u][1];
    		sz[u] = sz[lc] + sz[rc] + 1;
    		sum[u] = sum[lc] + sum[rc] + pri[u] * (sz[lc] + 1) * (sz[rc] + 1);
    	}
    	int build(int l,int r){
    		if(l == r)return create(l);
    		int m = (l + r) >> 1;
    		int u = create(m);
    		if(l <= m - 1)c[u][0] = build(l,m - 1);
    		if(m + 1 <= r)c[u][1] = build(m + 1,r);
    		pushup(u);
    		return u;
    	}
    	int merge(int u,int v){
    		if(!u || !v)return u + v;
    		if(pri[u] > pri[v]){
    			c[u][1] = merge(c[u][1],v);
    			pushup(u);
    			return u;
    		}
    		else{
    			c[v][0] = merge(u,c[v][0]);
    			pushup(v);
    			return v;
    		}
    	}
    	void split(int u,int x,int &v,int &w){
    		if(!u)v = w = 0;
    		else{
    			if(val[u] <= x){
    				v = u;	
    				split(c[v][1],x,c[v][1],w);
    			}
    			else{
    				w = u;
    				split(c[w][0],x,v,c[w][0]);
    			}
    			pushup(u);
    		}
    	}
    	void ud(int i,int x){ //将h[i]修改为x
    		int u,v,w;
    		split(rt,i,u,w);
    		split(u,i - 1,u,v);
    		pri[v] = x;
    		pushup(v);
    		rt = merge(merge(u,v),w);
    	}
    	ll query(){
    		return sum[rt];
    	}
    }T;
    
    pll p[N+5];
    
    int main(){
    	R = read();C = read();n = read();
    	for(rg int i = 1;i <= n;i++){
    		p[i].second = read(),p[i].first = read();
    	}
    	T.rt = T.build(1,R);
    	sort(p + 1,p + n + 1);
    	int cur = 0;
    	ll ans = 0;
    	for(rg ll cury = 1;cury <= C;cury++){
    		while(cur < n && p[cur+1].first == cury){
    			cur++;
    			T.ud(p[cur].second,cury);
    		}
    		ans += cury * R * (R + 1) / 2;
    		ans -= T.query();
    	}
    	ans = R * (R + 1) / 2 * C * (C + 1) / 2 - ans;
    	cout << ans << endl;
    	return 0;
    }
    
  • 相关阅读:
    jqgrid addRowData报错
    LINE 项目总结
    bootstrap-table 常用总结-树形结构(展开和折叠)
    bootstrap-table 常用总结-树形结构
    为wordpress的分类以及子分类指定固定模版
    wordpress主题开发-部分函数调用整理
    superslide滚动插件使用记录-产品滚动-图片滚动
    4GL之Non-SCROLLING CURSOR
    asp.net报表结构学习记录
    HTML基础教程_1
  • 原文地址:https://www.cnblogs.com/xh092113/p/12387487.html
Copyright © 2020-2023  润新知