• 「雅礼集训 2017 Day1」市场


    题目

    传送门

    解法

    由于有区间加,直接暴力做除法的复杂度是不对的。

    不过我们可以尝试将其转化为区间加法:若对于某个区间的 (p=max,q=min),有

    [Delta =p-left lfloorfrac{p}{d} ight floor=q-left lfloorfrac{q}{d} ight floor ]

    那么整个区间相当于减去 (Delta)

    具体可以从这个角度理解:(y=x) 的增长速度比 (y=left lfloorfrac{x}{d} ight floor,dge 1) 更快,所以当 (x) 变大时,(Delta) 也在变大。

    但如果不满足这个条件,复杂度看似仍然会炸啊?

    我们考虑用势能分析解决这个问题。定义一个代表 ([l,r]) 区间的节点的势能为 (log(max_{l,r}-min_{l,r}))。整棵树的势能就是所有节点的势能之和。

    查询操作并不引起势能变化,而且复杂度是显然的,不在我们的考虑范围之内。

    对于区间修改,暂时先考虑不完全被 ([l,r]) 覆盖的区间的节点的势能变化:单个节点最坏情况增加 (log V) 的势能((V) 是值域),这样的区间等价于分别包含 (l,r) 端点的区间,所以是 (log n) 级别的。那么总共只会增加 (qcdot log nlog V) 的势能。

    对于完全被 ([l,r]) 覆盖的区间的节点,考虑一棵二叉树的节点个数大概是二倍值域大小,所以完全被 ([l,r]) 覆盖的区间的节点个数在 (r-l+1) 级别。需要分两种操作讨论:

    • 区间加。显然势能是不变的。总时间复杂度 (mathcal O(qlog n))
    • 区间除。
      • (max_{L,R}-min_{L,R}le 1)。设满足此条件的节点个数为 (t)。此时势能极小而且基本不变了,而且它大概率满足上文优化的条件。所以我们认为它是 (mathcal O(log n)) 级别的。
      • (max_{L,R}-min_{L,R}> 1)。由于 (d>1),那么 (max_{L,R}-min_{L,R}) 至少减半,也即势能至少减 (1)。修改这部分节点复杂度是 (mathcal O(r-l+1-t)) 级别的,但同时势能至少减少 (r-l+1-t)。当减少到 (max_{L,R}-min_{L,R}le 1) 时,就又变成了上面的问题。

    所以总复杂度应该也就是积蓄的最大势能:(mathcal O(qcdot log nlog V))

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <cmath>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    
    const int maxn=1e5+5;
    
    int a[maxn];
    struct node {
    	ll s;
    	int tag,mx,mn;
    } t[maxn<<2];
    
    void pushUp(int o) {
    	t[o].s=t[o<<1].s+t[o<<1|1].s;
    	t[o].mx=max(t[o<<1].mx,t[o<<1|1].mx);
    	t[o].mn=min(t[o<<1].mn,t[o<<1|1].mn);
    }
    
    void build(int o,int l,int r) {
    	if(l==r) {
    		t[o].s=t[o].mx=t[o].mn=a[l];
    		return;
    	}
    	int mid=l+r>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    	pushUp(o);
    }
    
    void pushDown(int o,int L,int R) {
    	if(!t[o].tag)
    		return;
    	int l=o<<1,r=o<<1|1,mid=L+R>>1;
    	t[l].s+=1ll*t[o].tag*(mid-L+1),t[r].s+=1ll*t[o].tag*(R-mid);
    	t[l].mx+=t[o].tag,t[r].mx+=t[o].tag;
    	t[l].mn+=t[o].tag,t[r].mn+=t[o].tag;
    	t[l].tag+=t[o].tag,t[r].tag+=t[o].tag;
    	t[o].tag=0;
    }
    
    void div(int o,int l,int r,int L,int R,int d) {
    	if(l>R or r<L) return;
    	if(l>=L and r<=R) {
    		int p=t[o].mx-(int)floor(1.0*t[o].mx/d);
    		int q=t[o].mn-(int)floor(1.0*t[o].mn/d);
    		if(p==q) {
    			t[o].s-=1ll*p*(r-l+1);
    			t[o].mx-=p,t[o].mn-=p;
    			t[o].tag-=p;
    			return;
    		}
    	}
    	int mid=l+r>>1;
    	pushDown(o,l,r);
    	div(o<<1,l,mid,L,R,d);
    	div(o<<1|1,mid+1,r,L,R,d);
    	pushUp(o);
    }
    
    void modify(int o,int l,int r,int L,int R,int d) {
    	if(l>R or r<L) return;
    	if(l>=L and r<=R) {
    		t[o].s+=1ll*(r-l+1)*d;
    		t[o].mn+=d,t[o].mx+=d;
    		t[o].tag+=d;
    		return;
    	}
    	int mid=l+r>>1;
    	pushDown(o,l,r);
    	modify(o<<1,l,mid,L,R,d);
    	modify(o<<1|1,mid+1,r,L,R,d);
    	pushUp(o);
    }
    
    ll ask(int o,int l,int r,int L,int R) {
    	if(l>R or r<L) return 0;
    	if(l>=L and r<=R) return t[o].s;
    	int mid=l+r>>1;
    	pushDown(o,l,r);
    	return ask(o<<1,l,mid,L,R)+
    		ask(o<<1|1,mid+1,r,L,R);
    }
    
    int m_ask(int o,int l,int r,int L,int R) {
    	if(l>R or r<L) return 2e9;
    	if(l>=L and r<=R) return t[o].mn;
    	int mid=l+r>>1;
    	pushDown(o,l,r);
    	return min(m_ask(o<<1,l,mid,L,R),
    		m_ask(o<<1|1,mid+1,r,L,R));
    }
    
    int main() {
    	int n=read(9),q=read(9);
    	for(int i=1;i<=n;++i)
    		a[i]=read(9);
    	build(1,1,n);
    	int op,l,r,x;
    	while(q--) {
    		op=read(9);
    		l=read(9)+1,r=read(9)+1;
    		if(op==1) 
    			modify(1,1,n,l,r,read(9));
    		else if(op==2)
    			div(1,1,n,l,r,read(9));
    		else if(op==3)
    			print(m_ask(1,1,n,l,r),'
    ');
    		else print(ask(1,1,n,l,r),'
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    git简单介绍
    ssh常用操作
    gentoo emerge简单用法
    golang程序因未知错误崩溃时如何记录异常
    RPC实现原理(HSF、dubbo) 从头开始(一)
    websocket
    tmpfs小结
    CURL常用命令
    SVN命令详解
    3.Linux Shell流程控制
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14074440.html
Copyright © 2020-2023  润新知