zoukankan      html  css  js  c++  java
  • [洛谷P4198][BZOJ2957]楼房重建(线段树)

    Description

    • 数轴上有\(n\)个建筑,初始高度均为\(0\)
    • \(m\)次操作,每次在位置\(x_i\)上修建高度为\(y_i\)的建筑,建筑的权值为\(y_i/x_i\),求以第一个高度\(>0\)的建筑开始,字典序最小的权值上升子序列的长度
    • \(n,m,x_i<=100000,y_i<=1000000000\)

    Solution

    • 维护线段树,考虑单点修改回溯时如何合并
    • \(val[p]\)表示节点\(p\)对应区间上建筑的最大高度
    • \(p\)对应区间\([l,r]\),区间的中点为\(mid\)\(ans[p]\)为对应区间的答案,\(p\)的左右子节点分别为\(p2,p3\)
    • 显然\([l,mid]\)中的最大值一定在答案中,所以\([mid+1,r]\)中对答案贡献的子序列的开头一定\(>val[p2]\)
    • 于是有\(ans[p]=ans[p2]+calc(p3,val[p2])\)\(calc\)表示区间\(p3\)中,第一个数\(>val[p2]\)且字典序最小的上升子序列
    • 问题转化为求\(calc(p,v)\),分三类讨论:
    • (1).\(p\)是叶子节点,如果\(val[p]>v\)返回\(1\),否则返回\(0\)
    • (2).\(val[p2]>v\),回到上文,注意到回溯时\(ans[p3]\)\(calc(p3,val[p2])\)先计算,那么\(p3\)\(ans[p]\)的贡献和对\(calc(p,v)\)的贡献是一样的(因为这个贡献只和\(val[p2]\)有关,与\(v\)无关),贡献即\(ans[p]-ans[p2]\),那么返回\(ans[p]-ans[p2]+calc(p2,v)\)
    • (3).\(val[p2]<=v\),那么\(p2\)\(calc(p,v)\)没有贡献,返回\(calc(p3,v)\)
    • 时间复杂度\(O(nlog^2n)\)

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define p2 p << 1
    #define p3 p << 1 | 1
    
    template <class t>
    inline void read(t & res)
    {
    	char ch;
    	while (ch = getchar(), !isdigit(ch));
    	res = ch ^ 48;
    	while (ch = getchar(), isdigit(ch))
    	res = res * 10 + (ch ^ 48);
    }
    
    const int e = 1e5 + 5;
    int ans[e * 4], n, m;
    double val[e * 4];
    bool bo[e * 4];
    
    inline void build(int l, int r, int p)
    {
    	if (l == r)
    	{
    		bo[p] = 1;
    		return;
    	}
    	int mid = l + r >> 1;
    	build(l, mid, p2);
    	build(mid + 1, r, p3);
    }
    
    inline int calc(int p, double v)
    {
    	if (bo[p]) return val[p] > v;
    	if (val[p2] > v) return ans[p] - ans[p2] + calc(p2, v);
    	else return calc(p3, v);
    }
    
    inline void collect(int p)
    {
    	val[p] = max(val[p2], val[p3]);
    	ans[p] = ans[p2] + calc(p3, val[p2]);
    }
    
    inline void update(int l, int r, int s, double v, int p)
    {
    	if (l == r)
    	{
    		val[p] = v;
    		ans[p] = 1;
    		return;
    	}
    	int mid = l + r >> 1;
    	if (s <= mid) update(l, mid, s, v, p2);
    	else update(mid + 1, r, s, v, p3);
    	collect(p);
    }
    
    int main()
    {
    	int x, y;
    	read(n); read(m);
    	build(1, n, 1);
    	while (m--)
    	{
    		read(x);
    		read(y);
    		update(1, n, x, (double)y / (double)x, 1);
    		printf("%d\n", ans[1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    【长安十二时辰】 刺客信条风格剪辑
    【Oracle 触发器】(6)触发器应用场景--数据的备份和同步
    【Oracle 触发器】(5)触发器应用场景--数据库的审计
    银行窗口取号系统,共享数据的线程安全性
    模板方法
    创建线程时如果既传入了runnable对象,又继承thread重写了run方法,会执行哪里的代码
    启动线程用start方法而不是run方法
    java的回调
    java中的闭包
    静态内部类
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196528.html
Copyright © 2011-2022 走看看