• 【uoj#228】基础数据结构练习题 线段树+均摊分析


    题目描述

    给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有三种:区间加、区间开根、区间求和。

    $n,m,a_ile 100000$ 。


    题解

    线段树+均摊分析

    对于原来的两个数 $a$ 和 $b$ ( $a>b$ ) ,开根后变成 $sqrt a$ 和 $sqrt b$ ,它们的差从 $a-b$ 变成了 $sqrt a-sqrt b$ 。

    又有 $(sqrt a-sqrt b)(sqrt a+sqrt b)=a-b$ ,因此开方后的差小于原来差的开方。

    而当区间差为 $0$ 或 $a=x^2,b=x^2-1$ 的 $1$ 时,区间开根就变成了区间减。

    因此一个区间开根 $loglog(Max-Min)$ 次后就不需要暴力开根,直接区间减即可。

    定义线段树节点势能为 $loglog(Max-Min)$ ,那么每次对 $[l,r]$ 开根就是将所有 $lle x,yle r$ ,且势能不为 $0$ 的节点 $[x,y]$ 的势能减 $1$ ,代价为势能减少总量。

    分析区间加操作:只会修改到经过的节点的势能,影响 $log$ 个节点,将这些点的势能恢复为 $loglog(Max-Min)$ 。

    因此总的时间复杂度就是总势能量 $O((n+mlog n)loglog a)$ 。

    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    #define N 100010
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    using namespace std;
    typedef long long ll;
    ll sum[N << 2] , mx[N << 2] , mn[N << 2] , tag[N << 2];
    inline void add(ll v , int l , int r , int x)
    {
    	sum[x] += v * (r - l + 1) , mx[x] += v , mn[x] += v , tag[x] += v;
    }
    inline void pushup(int x)
    {
    	sum[x] = sum[x << 1] + sum[x << 1 | 1];
    	mx[x] = max(mx[x << 1] , mx[x << 1 | 1]);
    	mn[x] = min(mn[x << 1] , mn[x << 1 | 1]);
    }
    inline void pushdown(int l , int r , int x)
    {
    	if(tag[x])
    	{
    		int mid = (l + r) >> 1;
    		add(tag[x] , lson) , add(tag[x] , rson); 
    		tag[x] = 0;
    	}
    }
    inline void build(int l , int r , int x)
    {
    	if(l == r)
    	{
    		scanf("%lld" , &sum[x]) , mx[x] = mn[x] = sum[x];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(lson) , build(rson);
    	pushup(x);
    }
    inline void update(int b , int e , ll a , int l , int r , int x)
    {
    	if(b <= l && r <= e)
    	{
    		add(a , l , r , x);
    		return;
    	}
    	pushdown(l , r , x);
    	int mid = (l + r) >> 1;
    	if(b <= mid) update(b , e , a , lson);
    	if(e > mid) update(b , e , a , rson);
    	pushup(x);
    }
    inline void change(int b , int e , int l , int r , int x)
    {
    	if(b <= l && r <= e && mx[x] - (ll)sqrt(mx[x]) == mn[x] - (ll)sqrt(mn[x]))
    	{
    		add((ll)sqrt(mx[x]) - mx[x] , l , r , x);
    		return;
    	}
    	pushdown(l , r , x);
    	int mid = (l + r) >> 1;
    	if(b <= mid) change(b , e , lson);
    	if(e > mid) change(b , e , rson);
    	pushup(x);
    }
    inline ll query(int b , int e , int l , int r , int x)
    {
    	if(b <= l && r <= e) return sum[x];
    	pushdown(l , r , x);
    	int mid = (l + r) >> 1;
    	ll ans = 0;
    	if(b <= mid) ans += query(b , e , lson);
    	if(e > mid) ans += query(b , e , rson);
    	return ans;
    }
    int main()
    {
    	int n , m , opt , x , y;
    	ll z;
    	scanf("%d%d" , &n , &m);
    	build(1 , n , 1);
    	while(m -- )
    	{
    		scanf("%d%d%d" , &opt , &x , &y);
    		if(opt == 1) scanf("%lld" , &z) , update(x , y , z , 1 , n , 1);
    		else if(opt == 2) change(x , y , 1 , n , 1);
    		else printf("%lld
    " , query(x , y , 1 , n , 1));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Mysql的select加锁分析
    浅析Kubernetes的工作原理
    HTTP/2部署使用
    Amazon新一代云端关系数据库Aurora
    为什么 kubernetes 天然适合微服务
    深入解读Service Mesh背后的技术细节
    微服务的接入层设计与动静资源隔离
    Prim算法和Kruskal算法介绍
    DAG及拓扑排序
    BFS和DFS
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8709518.html
Copyright © 2020-2023  润新知