zoukankan      html  css  js  c++  java
  • AHOI2014 奇怪的计算器 和 HDU5306 Gorgeous Sequence

    线段树秀操作题。

    奇怪的计算器

    有 N 个数,一共会对这 N 个数执行 M 个指令(对没个数执行的指令都一样),每一条指令可以是以下四种指令之一:(这里 a 表示一个正整数)

    1. 加上 a
    2. 减去 a
    3. 乘以 a
    4. 加上 a*X(X 是数最开始的初值)

    该计算器有个奇怪的特点。每进行一个指令,若结果大于 R则变成 R,同理若结果小于 L,则变成 L。求这 N 个数最后的结果。

    N, M ≤ 200000

    题解

    重要性质:无论题目里面的修改怎么执行,所有数的相对大小(即排名)不变。所以我们每次的chkmin和chkmax操作肯定是对一个排序后的后缀和前缀区间进行的。那么我们成功地将不连续的问题转化成了连续的问题。

    所以我们可以针对题目设计懒标记 x=t1*x+t2*x0+t3。懒标记的合并很显然,而由于排名不变,所以区间的最大最小值还是很好维护的。

    时间复杂度 O(n log n)。

    CO int N=100000+10;
    int L,R;
    pair<int,int> p[N],q[N];
    
    LL t1[4*N],t2[4*N],t3[4*N];
    LL lp[4*N],rp[4*N],mn[4*N],mx[4*N];
    #define lc (x<<1)
    #define rc (x<<1|1)
    void build(int x,int l,int r){
    	t1[x]=1,t2[x]=t3[x]=0;
    	lp[x]=mn[x]=q[l].first,rp[x]=mx[x]=q[r].first;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	build(lc,l,mid),build(rc,mid+1,r);
    }
    IN void push_up(int x){
    	mn[x]=mn[lc],mx[x]=mx[rc];
    }
    IN void add_tag(int x,LL k1,LL k2,LL k3){
    	t1[x]*=k1,t2[x]=k1*t2[x]+k2,t3[x]=k1*t3[x]+k3;
    	mn[x]=k1*mn[x]+k2*lp[x]+k3,mx[x]=k1*mx[x]+k2*rp[x]+k3;
    }
    IN void push_down(int x){
    	if(t1[x]!=1 or t2[x] or t3[x]){
    		add_tag(lc,t1[x],t2[x],t3[x]);
    		add_tag(rc,t1[x],t2[x],t3[x]);
    		t1[x]=1,t2[x]=t3[x]=0;
    	}
    }
    void chkl(int x){
    	if(mn[x]>=L) return;
    	if(mx[x]<=L) return add_tag(x,0,0,L);
    	push_down(x);
    	if(mx[lc]<=L) add_tag(lc,0,0,L),chkl(rc);
    	else chkl(lc);
    	push_up(x);
    }
    void chkr(int x){
    	if(mx[x]<=R) return;
    	if(mn[x]>=R) return add_tag(x,0,0,R);
    	push_down(x);
    	if(mn[rc]>=R) add_tag(rc,0,0,R),chkr(lc);
    	else chkr(rc);
    	push_up(x);
    }
    
    int ans[N];
    
    void query(int x,int l,int r){
    	if(l==r){
    		ans[q[l].second]=mn[x];
    		return;
    	}
    	push_down(x);
    	int mid=(l+r)>>1;
    	query(lc,l,mid),query(rc,mid+1,r);
    }
    
    int main(){
    	int n=read<int>();
    	read(L),read(R);
    	for(int i=1;i<=n;++i){
    		char opt[2];scanf("%s",opt);
    		if(opt[0]=='+') p[i].first=1;
    		else if(opt[0]=='-') p[i].first=2;
    		else if(opt[0]=='*') p[i].first=3;
    		else p[i].first=4;
    		read(p[i].second);
    	}
    	int m=read<int>();
    	for(int i=1;i<=m;++i) read(q[i].first),q[i].second=i;
    	sort(q+1,q+m+1);
    	build(1,1,m);
    	for(int i=1;i<=n;++i){
    		if(p[i].first==1) add_tag(1,1,0,p[i].second);
    		else if(p[i].first==2) add_tag(1,1,0,-p[i].second);
    		else if(p[i].first==3) add_tag(1,p[i].second,0,0);
    		else add_tag(1,1,p[i].second,0);
    		if(mx[1]>R) chkr(1);
    		if(mn[1]<L) chkl(1);
    	}
    	query(1,1,m);
    	for(int i=1;i<=m;++i) printf("%d
    ",ans[i]);
    	return 0;
    }
    

    Gorgeous Sequence

    There is a sequence a of length n. We use ai to denote the i-th element in this sequence. You should do the following three types of operations to this sequence.

    0 x y t: For every x≤i≤y, we use min(ai,t) to replace the original ai's value.
    1 x y: Print the maximum value of ai that x≤i≤y.
    2 x y: Print the sum of ai that x≤i≤y.

    分析

    吉司机线段树板子题。

    1和2操作很简单,0操作很难,一个一个改时间肯定不允许,但是又没什么办法可以直接全改掉。

    结合题目问的是区间最大值和区间和,那么就思考操作 0 会对区间最大值和区间和有什么影响,于是就有了以下方法:

    维护 3 个值,区间最大值,区间严格次大值,区间最大值的个数。然后我们每次做0操作的时候,就会有3种情况。

    1. t >= 区间最大值, 这时每个值都不用修改,直接返回。
    2. 区间次大值 < t < 区间最大值,此时只有最大值会变,又已经求得了最大值的个数,所以我们可以直接更新这段的sum和max。
    3. 其他情况。无法直接对当前情况修改,所以继续搜两个儿子,直到搜到前两种情况为止。

    我们可以把区间的最大值看做是这个区间的标记,因为每次更新的时候只会更新到(区间次大值 < t < 区间最大值)的情况,然后会修改sum和max,所以以后一旦进入这个区间的子树,必须先更新这个子树的max和sum。于是就有了pushdown()的写法,它就是负责把当前区间的sum和mx信息传给子树。

    const int MAXN=1e6+7;
    
    int ql,qr,v;
    
    struct SegTree
    {
    	ll sum[MAXN<<2];
    	int maxv[MAXN<<2],num[MAXN<<2],secv[MAXN<<2];
    #define lson (now<<1)
    #define rson (now<<1|1)
    	inline void pushup(int now)
    	{
    		sum[now]=sum[lson]+sum[rson];
    		maxv[now]=max(maxv[lson],maxv[rson]);
    		if(maxv[lson]==maxv[rson])
    		{
    			num[now]=num[lson]+num[rson];
    			secv[now]=max(secv[lson],secv[rson]);
    		}
    		else
    		{
    			num[now]=maxv[lson]>maxv[rson]?num[lson]:num[rson];
    			secv[now]=max(secv[lson],secv[rson]);
    			secv[now]=max(secv[now],min(maxv[lson],maxv[rson]));
    		}
    	}
    	
    	void build(int now,int l,int r)
    	{
    		if(l==r)
    		{
    			sum[now]=_read();
    			maxv[now]=sum[now];
    			num[now]=1;
    			secv[now]=-1;
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(lson,l,mid);
    		build(rson,mid+1,r);
    		pushup(now);
    	}
    	
    	inline void puttag(int now,int x)
    	{
    		if(x>=maxv[now])
    			return;
    		sum[now]-=(ll)num[now]*(maxv[now]-x);
    		maxv[now]=x;
    	}
    	
    	inline void pushdown(int now)
    	{
    		puttag(lson,maxv[now]);
    		puttag(rson,maxv[now]);
    	}
    	
    	void change(int now,int l,int r)
    	{
    		if(v>=maxv[now])
    			return;
    		if(ql<=l&&r<=qr&&secv[now]<v)
    		{
    			puttag(now,v);
    			return;
    		}
    		pushdown(now);
    		int mid=(l+r)>>1;
    		if(ql<=mid)
    			change(lson,l,mid);
    		if(qr>=mid+1)
    			change(rson,mid+1,r);
    		pushup(now);
    	}
    	
    	ll qmax(int now,int l,int r)
    	{
    		if(ql<=l&&r<=qr)
    		{
    			return maxv[now];
    		}
    		pushdown(now);
    		int mid=(l+r)>>1;
    		ll ans=0;
    		if(ql<=mid)
    			ans=max(ans,qmax(lson,l,mid));
    		if(qr>=mid+1)
    			ans=max(ans,qmax(rson,mid+1,r));
    		return ans;
    	}
    	
    	inline ll qsum(int now,int l,int r)
    	{
    		if(ql<=l&&r<=qr)
    		{
    			return sum[now];
    		}
    		pushdown(now);
    		int mid=(l+r)>>1;
    		ll ans=0;
    		if(ql<=mid)
    			ans+=qsum(lson,l,mid);
    		if(qr>=mid+1)
    			ans+=qsum(rson,mid+1,r);
    		return ans;
    	}
    }Tree;
    
    int n,m;
    
    int main()
    {
    //  freopen(".in","r",stdin);
    //  freopen(".out","w",stdout);
    	int T=_read();
    	while(T--)
    	{
    		n=_read();m=_read();
    		Tree.build(1,1,n);
    		while(m--)
    		{
    			int opt=_read();
    			if(opt==0)
    			{
    				ql=_read();qr=_read();v=_read();
    				Tree.change(1,1,n);
    			}
    			else if(opt==1)
    			{
    				ql=_read();qr=_read();
    				printf("%lld
    ",Tree.qmax(1,1,n));
    			}
    			else if(opt==2)
    			{
    				ql=_read();qr=_read();
    				printf("%lld
    ",Tree.qsum(1,1,n));
    			}
    		}
    	}
    //  fclose(stdin);
    //  fclose(stdout);
        return 0;
    }
    

    BZOJ4695 最假女选手是这道题的复杂版。

  • 相关阅读:
    [codevs2800]送外卖
    python JSON处理
    python系统编码格式
    python,django,mysql版本号查询
    django开发总结:
    python之---类和实例
    django Q和F查询
    合并多个python list以及合并多个 django QuerySet 的方法
    python学习之---匿名函数,返回函数,偏函数
    python学习之---生成器
  • 原文地址:https://www.cnblogs.com/autoint/p/9689345.html
Copyright © 2011-2022 走看看