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 最假女选手是这道题的复杂版。

  • 相关阅读:
    SP笔记:交叉实现七行并成一行
    HTML tag 学习
    操作哈希表
    Efficient bipedal robots based on passivedynamic walkers
    Pushing People Around
    ZEROMOMENT PONTTHIRTY FIVE YEARS OF ITS LIFE

    Active Learning for RealTime Motion Controllers
    Accelerometerbased User Interfaces for the Control of a Physically Simulated Character
    Dynamic Response for Motion Capture Animation
  • 原文地址:https://www.cnblogs.com/autoint/p/9689345.html
Copyright © 2011-2022 走看看