BZOJ3878 [Ahoi2014]奇怪的计算器 线段树

BZOJ3878 [Ahoi2014]奇怪的计算器

Description

对于一个储存数据大小只有[L,R]的计算器,有4个操作,
1、+ a:表示将当前的结果加上a;
2、- a:表示将当前的结果减去a;
3、* a:表示将当前的结果乘以a;
4、@ a:表示将当前的结果加上a*X(X是一开始输入的数)。
计算器每次会把超过R或低于L的储存为R或L继续后续计算。
给定n个操作,与q个数,求出每个数计算后的答案。

Input

输入文件的第一行包含三个正整数,N,L和R;
第接下来N行,每行一个指令,每个指令如题述,由一个字符和一个正整
数组成,字符和正整数中间有一个空格隔开;
第N+2行包含一个整数Q,表示输入的数的数量;
第接下来Q行每行一个正整数,第k个正整数Xk表示在第k次输入的
整数。

Output

输出Q行每行一个正整数,第k行的整数表示输入Xk后,依次经过N个指
令进行计算所得到的结果。

Sample Input
5 1 6
+ 5
- 3
* 2
- 7
@ 2
3
2
1
5
Sample Output
5
3
6
HINT

1<=N,Q<= 105 ,1<=L<= Xk <=R<= 109 ,1<=a<= 109

题解

要对全部的数进行操作,还得改变一些溢出数。
我们可以发现,这些操作并不会改变输入的数的相对大小,所以我们可以先排一遍序,这样就保证了溢出的数是连续的一段,就可以用线段树来维护这个序列。

再维护溢出的数时,如果发现某一个区间的最小值比R还大,就代表要把这个区间更新为R。同样的,如果发现某一个区间的最大值比L还小,就改成L。

小(hei)技(ke)巧(ji):我们可以不用再新建一个标记来改值,就只要把乘法标记变为0,再把加法标记变成要要变的值就行。

本入代码奇丑无比,不喜勿喷

#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#define MAXN 100000+10
#define LL long long
using namespace std;
int n,m,maxd;
LL mi[MAXN<<2],mx[MAXN<<2],add[MAXN<<2],times[MAXN<<2],add2[MAXN<<2];
LL lm,rm;
struct Op{
    LL op,d;
}op[MAXN];
struct Date{
    LL d,n;
}a[MAXN];
bool cmp1(const Date a,const Date b) {return a.d<b.d;}
bool cmp2(const Date a,const Date b) {return a.n<b.n;}
void pushup(int rt)
{
    mi[rt]=min(mi[rt<<1],mi[rt<<1|1]);
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}

void pushdown(int rt,int l,int r)
{
    if(add[rt]||times[rt]!=1||add2[rt])
    {
        int m=(l+r)>>1;
        mi[rt<<1]=(mi[rt<<1]*times[rt]+add[rt]+add2[rt]*a[l].d);
        mi[rt<<1|1]=(mi[rt<<1|1]*times[rt]+add[rt]+add2[rt]*a[m+1].d);  
        mx[rt<<1]=(mx[rt<<1]*times[rt]+add[rt]+add2[rt]*a[m].d);
        mx[rt<<1|1]=(mx[rt<<1|1]*times[rt]+add[rt]+add2[rt]*a[r].d);        
        add[rt<<1]=(add[rt<<1]*times[rt]+add[rt]);
        add[rt<<1|1]=(add[rt<<1|1]*times[rt]+add[rt]);
        add2[rt<<1]=(add2[rt<<1]*times[rt]+add2[rt]);
        add2[rt<<1|1]=(add2[rt<<1|1]*times[rt]+add2[rt]);
        times[rt<<1]=(times[rt]*times[rt<<1]);
        times[rt<<1|1]=(times[rt]*times[rt<<1|1]);
        times[rt]=1;add[rt]=0;add2[rt]=0;
    }
}

void build(int rt,int l,int r)
{
    if(l==r)
    {
        mi[rt]=mx[rt]=a[l].d;
        maxd=max(maxd,rt);
        return ;
    }
    int m=(l+r)>>1;
    build(rt<<1,l,m);
    build(rt<<1|1,m+1,r);
    pushup(rt);
}

void Add(int L,int R,LL x,int l,int r,int rt)
{   
    if(L<=l&&r<=R)
    {
        mi[rt]+=x;
        mx[rt]+=x;
        add[rt]=add[rt]+x;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(rt,l,r);
    if(L<=m) Add(L,R,x,l,m,rt<<1);
    if(R>m) Add(L,R,x,m+1,r,rt<<1|1);
    pushup(rt);
}
void Add2(int L,int R,LL x,int l,int r,int rt)
{   
    if(L<=l&&r<=R)
    {
        mi[rt]+=x*a[l].d;
        mx[rt]+=x*a[r].d;
        add2[rt]=add2[rt]+x;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(rt,l,r);
    if(L<=m) Add(L,R,x,l,m,rt<<1);
    if(R>m) Add(L,R,x,m+1,r,rt<<1|1);
    pushup(rt);
}

void Mui(int L,int R,LL x,int l,int r,int rt)
{   
    if(L<=l&&r<=R)
    {
        mi[rt]*=x;
        mx[rt]*=x;
        add[rt]=(add[rt]*x);
        add2[rt]=(add2[rt]*x);
        times[rt]=(x*times[rt]);
        return ;
    }
    int m=(l+r)>>1;
    pushdown(rt,l,r);
    if(L<=m) Mui(L,R,x,l,m,rt<<1);
    if(R>m) Mui(L,R,x,m+1,r,rt<<1|1);
    pushup(rt);
}
void fixed(int L,int R,int l,int r,int rt)
{   
    if(lm<=mi[rt]&&mx[rt]<=rm) return ;
        if(mx[rt]<lm)
        {
            mi[rt]=mx[rt]=add[rt]=lm;
            add2[rt]=times[rt]=0;
            return; 
        }else
        if(mi[rt]>rm)
        {
            mi[rt]=mx[rt]=add[rt]=rm;
            add2[rt]=times[rt]=0;
            return ;
        }
    if(l==r) return ;
    int m=(l+r)>>1;
    pushdown(rt,l,r);
    if(L<=m) fixed(L,R,l,m,rt<<1);
    if(R>m) fixed(L,R,m+1,r,rt<<1|1);
    pushup(rt);
}

LL query(int p,int l,int r,int rt)
{   
    if(l==r&&l==p)
        return mi[rt];
    int m=(l+r)>>1;LL ans=0;
    pushdown(rt,l,r);
    if(p<=m) ans=query(p,l,m,rt<<1);
    else
    if(p>m) ans=query(p,m+1,r,rt<<1|1);
    return ans;
}
int main()
{
    scanf("%d%lld%lld",&m,&lm,&rm);
    for(int i=1;i<=m;i++)
    {
        char k[3];
        scanf("%s%lld",k,&op[i].d);
        if(k[0]=='+') op[i].op=1;
        else if(k[0]=='-') op[i].op=2;
        else if(k[0]=='*') op[i].op=3;
        else if(k[0]=='@') op[i].op=4;
    }
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {   
        scanf("%lld",&a[i].d);
        times[i]=times[i+n]=times[i+n*2]=times[i+n*3]=1;
        a[i].n=i;
    }
    sort(a+1,a+n+1,cmp1);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        if(op[i].op==1) {Add(1,n,op[i].d,1,n,1);}
        else if(op[i].op==2) {Add(1,n,-op[i].d,1,n,1);}
        else if(op[i].op==3) {Mui(1,n,op[i].d,1,n,1);}
        else if(op[i].op==4) {Add2(1,n,op[i].d,1,n,1);}
        fixed(1,n,1,n,1);
    }
    for(int i=1;i<=n;i++)
        a[i].d=query(i,1,n,1);
    sort(a+1,a+n+1,cmp2);
    for(int i=1;i<=n;i++)
        printf("%lld\n",a[i].d);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值