【BZOJ4370】【IOI2015】horses 数据结构 平衡树+线段树

4370: [IOI2015]horses马

Time Limit: 30 Sec Memory Limit: 1500 MB
Description

像他的祖先一样,Mansur喜欢繁殖马匹。目前,他拥有哈萨克斯坦最大的马场。以前情况可不是这样,N年前Mansur年轻时,他只拥有一匹马,但他一直梦想着成为富豪,最终,他美梦成真。
按照时间的先后顺序将年份编号为0到N-1(即N-1年是最近的一年)。每年的天气会影响马匹的繁殖。Mansur用一个正整数X[i]记录第i年的繁殖系数,如果第i年开始时有h匹马,那么这一年结束时会有h•X[i]匹马。
每年,只有年底的时候可以出售马匹。Mansur用一个正整数Y[i]记录第i年末卖出一匹马的售价。Mansur可以卖出任意多匹马,每匹售价均为Y[i]。
现在,Mansur想知道如果在N年中,他总能在最佳时间出售马匹,他能获得的最⼤收益是多少?你正好在Mansur家做客,所以他想请你帮他回答这个问题。
Mansur对记录下的X和Y做了M次更新,每次更新,Mansur要么改变一个X[i] ,要么改变一个Y[i]。每次更新后,他都会问你出售马匹能获得的最大收益。Mansur的更新是累加的,即你的每个回答时都应该考虑之前的所有更新。注意:某个X[i]或者Y[i]可能会被更新多次。
对于Mansur的问题,实际的答案可能是一个非常大的数字,你只要给出实际答案模10^9+7后的结果即可。

Input

第1行: N
第2行: X[0] … X[N - 1]
第3行: Y[0] … Y[N - 1]
第4行: M
第5,…,M+4行: 每行3个数字type pos val (type=1表示updateX,type=2 表
示updateY)。
N: 表示总共有N年。
X: 长度为N的数组,对0≤i≤N-1,X[i]表示i年的繁殖系数。
Y: 长度为N的数组,对0≤i≤N-1,Y[i]表示i年末出售一匹马的价格。
注意:X、Y均为Mansur给定的初值(更新前的值)。
pos: 一个整数,范围是0,…,N-1。
val: X[pos]或Y[pos]更新后的值。

Output

共M+1行
第1行:一个整数表示初始状态下,Mansur获得的最大收益模10^9+7后的值。
第2,…,M+1行:每行一个整数,表示这次更新后Mansur获得的最大收益模10^9+7后的值。

Sample Input

3

2 1 3

3 4 1

1

2 1 2
Sample Output

8

6
HINT
n<=500000,m<=100000
Source
IOI2015 day 1

这题杜教WC上讲了,当时听着自己又想了想感觉并不是很难(应该是那六道题中第二简单的……),回来后决定写一写,写着玩着竟然就是两天……
首先有这样一件事情:一定存在一个最优解,其在某个时刻将马全部卖出。
证明:设Si为x1,x2…xi的乘积。假设最优解是分两次,分别在a、b两时刻卖出,设a时刻卖出k匹马,则收益为k * ya-Sb * yb-k * Sb * yb / Sa。经过简单计算可发现:Sa * ya < Sb * yb时,则在a时刻将马全部卖出一定是最优解之一;Sa * y a>=Sb * yb时,在b时刻将马全部卖出一定是最优解之一。由此命题得证。
为了方便,我们约定x0=1。
设lim=10^9,p=lim+7。
接下来问题便转化为了:求max{Si * yi,1<=i<=n },有修改。由于Si的值太大,难以用数据结构直接维护。根据数据范围,不难发现答案只存在于某个后缀j中,后缀j满足:后j+1个数的乘积mul<=lim,且mul * xj>lim。如果我们把大于1的数成为“wxw数”,不难发现每次修改,后缀j只会增加或减少至多30个wxw数(因为2^30>lim)。由此我们便可以利用双向链表来维护所有的wxw数(同时我们需要一颗平衡树来维护这个双向链表……),同时使用线段树维护后缀j中的Si*yi的最大值(这里的Si从j+1开始计算),同时手动维护前缀j的乘积%p的值(用到了扩展欧几里得算法求逆元,也可用费马小定理)。
btw,平衡树可以用set代替毕竟只需要寻找前驱后继,然而极度讨厌STL的我……
编写的过程中一定要注意一些可能使中间运算结果超出long long 范围的细节。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define clr(a,b) memset(a,b,sizeof(a))
#define maxn 500000
#define max(a,b) (a>b?a:b)
#define mid l+r>>1
using namespace std;
typedef long long LL;
const LL p=1e9+7;
const LL lim=1e9;
int n;
LL x[maxn+10],y[maxn+10];
void exeuclid(LL a,LL b,LL &x,LL &y){
    if(b==0){
        x=1;
        y=0;
    }else{
        exeuclid(b,a%b,y,x);
        y-=a/b*x;
    }
}
LL inv(LL a){
    LL x,y;
    exeuclid(a,p,x,y);
    return (x+p)%p;
}
struct doublelinkedlist{
    int fa[maxn+10],ch[maxn+10][2],ro,pre[maxn+10],suf[maxn+10];
    void init(){
        clr(fa,0);
        clr(pre,0);
        clr(suf,0);
        clr(ch,0);
        ro=1;
        fa[n+2]=1;
        ch[1][1]=n+2;
        pre[n+2]=1;
        suf[1]=n+2;
        for(int i=2;i<=n+1;i++)if(x[i]!=1)insert(ro,0,i);
    }
    int rc(int fa,int o){return ch[fa][1]==o;}
    void rot(int o){
        int f=fa[o],r=rc(f,o);
        fa[ch[fa[f]][rc(fa[f],f)]=o]=fa[f];
        fa[ch[f][r]=ch[o][r^1]]=f;
        fa[ch[o][r^1]=f]=o;
    }
    void splay(int o,int f){
        if(!f)ro=o;
        while(fa[o]!=f){
            if(fa[fa[o]]!=f&&rc(fa[fa[o]],fa[o])==rc(fa[o],o))rot(fa[o]);
            rot(o);
        }
    }
    void del(int o){
        int p=pre[o],s=suf[o];
        splay(p,0);
        splay(s,p);
        ch[s][0]=0;
        suf[p]=s;
        pre[s]=p;
    }
    void insert(int &o,int f,int pos){
        if(!o){
            o=pos;
            fa[o]=f;
            ch[o][0]=ch[o][1]=0;
            splay(pos,0);
            int p=ch[pos][0],s=ch[pos][1];
            while(ch[p][1])p=ch[p][1];
            while(ch[s][0])s=ch[s][0];
            pre[pre[s]=pos]=p;
            suf[suf[p]=pos]=s;
        }else{
            if(pos>o)insert(ch[o][1],o,pos);
            else insert(ch[o][0],o,pos);
        }
    }
}spt;
struct segnode{
    int lc,rc;
    LL maxv,mulv;
};
struct segmenttree{
    segnode no[2*maxn+10];
    int cnt;
    void init(int &l,LL &pre,LL &suf){
        clr(no,0);
        cnt=0;
        int o;
        maketree(o,2,n+1);
        pre=suf=1;
        for(l=n+1;l>1;l--){
            suf*=x[l];
            if(suf>lim){
                suf/=x[l];
                for(int j=2;j<=l;j++)pre=pre*x[j]%p;
                break;
            }
            update(1,2,n+1,l,n+1,x[l],1);
        }
        for(int i=2;i<=n+1;i++)update(1,2,n+1,i,i,y[i],1);  
    }
    void maketree(int &o,int l,int r){
        o=++cnt;
        no[o].mulv=1;
        no[o].maxv=1;
        if(l==r)return;
        int m=mid;
        maketree(no[o].lc,l,m);
        maketree(no[o].rc,m+1,r);   
    }
    void mark(int o,LL v){
        segnode &cur=no[o];
        cur.mulv*=v;
        cur.maxv*=v;
    }
    void update(int o,int l,int r,int ql,int qr,LL v1,LL v2){
        if(ql<=l&&r<=qr){
            no[o].maxv/=v2;
            no[o].mulv/=v2;
            mark(o,v1);
        }else{
            segnode &cur=no[o];
            int m=mid;
            if(ql<=m)update(cur.lc,l,m,ql,qr,v1,v2);
            if(qr>m)update(cur.rc,m+1,r,ql,qr,v1,v2);   
            cur.maxv=max(no[cur.lc].maxv,no[cur.rc].maxv)*cur.mulv;
        }
    }
    void query(int o,int l,int r,int ql,int qr,LL mul,LL &qmax){
        if(ql<=l&&r<=qr)qmax=max(qmax,no[o].maxv*mul);
        else{
            int m=mid;
            if(ql<=m)query(no[o].lc,l,m,ql,qr,mul*no[o].mulv,qmax);
            if(qr>m)query(no[o].rc,m+1,r,ql,qr,mul*no[o].mulv,qmax);
        }
    }
}seg;
LL pre,suf;
int l;
void print(){
    LL qmax=y[l],ans;
    seg.query(1,2,n+1,l+1,n+1,1,qmax);  
    printf("%lld\n",qmax%p*pre%p);
}
int main(){
    freopen("data53.in","r",stdin);
    freopen("4370.out","w",stdout);
    scanf("%d",&n);
    for(int i=2;i<=n+1;i++)scanf("%lld",&x[i]);
    x[1]=x[n+2]=1;
    for(int i=2;i<=n+1;i++)scanf("%lld",&y[i]);
    seg.init(l,pre,suf);
    spt.init();
    print();
    int q;
    scanf("%d",&q);
    int k,pos;
    LL v;
    while(q--){
        scanf("%d%d%lld",&k,&pos,&v);
        pos+=2;
        if(k==1){
            if(x[pos]==1&&v!=1)spt.insert(spt.ro,0,pos);
            if(x[pos]!=1&&v==1)spt.del(pos);
            if(pos<=l){
                pre=pre*inv(x[pos])%p*v%p;
                x[pos]=v;
            }else{
                seg.update(1,2,n+1,pos,n+1,1,x[pos]);
                suf=suf/x[pos]*v;
                x[pos]=v;
            }
            while(suf>lim){
                int s=spt.suf[l];
                suf/=x[s];
                pre=pre*x[s]%p;
                if(s!=pos)seg.update(1,2,n+1,s,n+1,1,x[s]);
                l=s;
            }
            if(l<pos)seg.update(1,2,n+1,pos,n+1,x[pos],1);
            while(l!=1){
                if(suf*x[l]<=lim){
                    suf*=x[l];
                    pre=pre*inv(x[l])%p;
                    seg.update(1,2,n+1,l,n+1,x[l],1);
                    l=spt.pre[l];
                }else break;
            }
        }else{
            seg.update(1,2,n+1,pos,pos,v,y[pos]);
            y[pos]=v;
        }
        print();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值