[bzoj3878]奇怪的计算器

题目大意

一个计算器,数值范围在[l,r]。
有n条指令,每条指令:
1、把当前数x变成x+a
2、把当前数x变成x-a
3、把当前数x变成x*a
4、若一开始输入的数为t,把当前数x变成x+a*t
每次执行一条指令后,当前数需要对l取max,然后对r取min。
现在q次询问,问往计算器输入x得到什么?

平衡树维护函数

我们尝试建立一个自变量为输入的初始值因变量为执行完若干指令后的值得一个函数。
这个函数是分段函数,每一段均为一次函数。而且整个函数定义域为[l,r],其整体是单调的(易证对于 x<y ,执行若干指令后x’<=y’)。
用a和b表示一段,即y=ax+b
1、2、4操作都是对a或b进行增加。
3操作比较特殊,相当于a和b都进行乘法运算。
我们用平衡树维护函数,每个节点表示一段,平衡树的中序遍历对应节点的定义域是单调的。
我们要维护乘法标记与加法标记,并规定乘法标记优先级更高。
然后用平衡树维护一下四种指令。
对于对l取max和对r取min,因为函数单调,可以找到<=l的一段然后用一个新建节点代替,r同理。
写的丑啊……

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=200000+10;
int father[maxn],tree[maxn][2],L[maxn],R[maxn],sta[maxn];
ll a[maxn],b[maxn],c[maxn],ada[maxn],adb[maxn];
int i,j,k,l,r,t,n,m,q,tot,top,root,x;
char ch;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void mark(int x,ll v1,ll v2){
    a[x]+=v1;b[x]+=v2;
    ada[x]+=v1;adb[x]+=v2;
}
void mark2(int x,ll v){
    a[x]*=v;b[x]*=v;
    c[x]*=v;
    ada[x]*=v;
    adb[x]*=v;
}
void clear(int x){
    if (c[x]>1){
        if (tree[x][0]) mark2(tree[x][0],c[x]);
        if (tree[x][1]) mark2(tree[x][1],c[x]);
        c[x]=1;
    }
    if (ada[x]||adb[x]){
        if (tree[x][0]) mark(tree[x][0],ada[x],adb[x]);
        if (tree[x][1]) mark(tree[x][1],ada[x],adb[x]);
        ada[x]=adb[x]=0;
    }
}
int pd(int x){
    return tree[father[x]][1]==x;
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    if (root==y) root=x;
}
void remove(int x,int y){
    top=0;
    while (x!=y){
        sta[++top]=x;
        x=father[x];
    }
    while (top){
        clear(sta[top]);
        top--;
    }
}
void splay(int x,int y){
    remove(x,y);
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
char get(){
    char ch=getchar();
    while (ch!='+'&&ch!='-'&&ch!='*'&&ch!='@') ch=getchar();
    return ch;
}
int find(int x){
    if (!x) return 0;
    clear(x);
    if (a[x]*(ll)L[x]+b[x]<=l&&a[x]*(ll)R[x]+b[x]>=l) return x;
    else if (a[x]*(ll)R[x]+b[x]<l){
        int t=find(tree[x][1]);
        if (t) return t;else return x;
    }
    else return find(tree[x][0]);
}
int find2(int x){
    if (!x) return 0;
    clear(x);
    if (a[x]*(ll)L[x]+b[x]<=r&&a[x]*(ll)R[x]+b[x]>=r) return x;
    else if (a[x]*(ll)R[x]+b[x]<r) return find2(tree[x][1]);
    else{
        int t=find2(tree[x][0]);
        if (t) return t;else return x;
    }
}
int getmin(int x){
    clear(x);
    if (!tree[x][0]) return x;
    else return getmin(tree[x][0]);
}
int getmax(int x){
    clear(x);
    if (!tree[x][1]) return x;
    else return getmax(tree[x][1]);
}
int query(int x,int y){
    clear(x);
    if (L[x]<=y&&R[x]>=y) return x;
    else if (R[x]<y) return query(tree[x][1],y);
    else return query(tree[x][0],y);
}
int main(){
    freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    m=read();l=read();r=read();
    root=tot=1;
    a[1]=1;b[1]=0;c[1]=1;
    L[1]=l;R[1]=r;
    while (m--){
        ch=get();x=read();
        if (ch=='+') mark(root,0,x);
        else if (ch=='-') mark(root,0,-x);
        else if (ch=='*') mark2(root,x);
        else mark(root,x,0);
        j=query(root,r);
        splay(j,0);
        clear(j);
        if (a[j]*(ll)r+b[j]<=l){
            root=++tot;
            a[tot]=0;b[tot]=l;c[tot]=1;
            L[tot]=l;R[tot]=r;
        }
        j=find(root);
        if (j){
            splay(j,0);
            clear(j);
            if (!a[j]) k=R[j];
            else k=int(((ll)l-b[j])/a[j]);
            if (k<R[j]){
                tree[j][0]=++tot;
                L[tot]=l;R[tot]=k;
                a[tot]=0;b[tot]=l;c[tot]=1;
                father[tot]=j;
                L[j]=k+1;
            }
            else if (!tree[j][1]){
                a[j]=0;b[j]=l;c[j]=1;
            }
            else{
                j=getmin(tree[j][1]);
                splay(j,0);
                tree[j][0]=++tot;
                L[tot]=l;R[tot]=k;
                a[tot]=0;b[tot]=l;c[tot]=1;
                father[tot]=j;
            }
        }
        j=query(root,l);
        splay(j,0);
        clear(j);
        if (a[j]*(ll)l+b[j]>=r){
            root=++tot;
            a[tot]=0;b[tot]=r;c[tot]=1;
            L[tot]=l;R[tot]=r;
        }
        j=find2(root);
        if (j){
            splay(j,0);
            clear(j);
            if (!a[j]) k=L[j];
            else if (((ll)r-b[j])%a[j]==0) k=int(((ll)r-b[j])/a[j]);else k=int(((ll)r-b[j])/a[j])+1;
            if (k>L[j]){
                tree[j][1]=++tot;
                L[tot]=k;R[tot]=r;
                a[tot]=0;b[tot]=r;c[tot]=1;
                father[tot]=j;
                R[j]=k-1;
            }
            else if (!tree[j][0]){
                a[j]=0;b[j]=r;c[j]=1;
            }
            else{
                j=getmax(tree[j][0]);
                splay(j,0);
                tree[j][1]=++tot;
                L[tot]=k;R[tot]=r;
                a[tot]=0;b[tot]=r;c[tot]=1;
                father[tot]=j;
            }
        }
    }
    q=read();
    while (q--){
        x=read();
        j=query(root,x);
        splay(j,0);
        clear(j);
        printf("%d\n",a[j]*(ll)x+b[j]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值