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;
}

【BZOJ4370】【IOI2015】horses 数据结构 平衡树+线段树相关推荐

  1. 307. Range Sum Query - Mutable | 307. 区域和检索 - 数组可修改(数据结构:线段树,图文详解)

    题目 https://leetcode.com/problems/range-sum-query-mutable/ 吐槽官方题解 这题的 英文版官方题解,配图和代码不一致,而且描述不清:力扣国内版题解 ...

  2. 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 2271  Solved: 935 [Submit][St ...

  3. 3682: Phorni 后缀平衡树 线段树

    国际惯例的题面: 考虑如果没有强制在线我们能怎么水掉这个题,先构造出字符串,各种方法求一下后缀数组,然后线段树维护区间rank最小的位置即可. 然而他要求强制在线,支持插入后缀,并比较后缀大小(求ra ...

  4. 数据结构之线段树入门(单点更新区间查询)

    线段树是学习数据结构必须学习的一种数据结构,在ACM,蓝桥等比赛中是经常出现的.利用线段树解题,会使得题目简单易理解.而且线段树是数据结构中比较基础而且用的很多的一种. 线段树定义 线段树是一种二叉搜 ...

  5. c++ 数据结构之 线段树

    线段树是一种数据结构,是一种二叉树.线段树将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.线段树对于区间求和等区间操作能够实现复杂度为O(logn)的操作,故得以广泛利用.修改一个 ...

  6. 【数据结构】线段树的扩展与应用

    线段树是一种非常基础的数据结构,但有的时候仅仅是普通的线段树无法满足需求,那么我们就要对其进行一些扩展. Chapter1:标记永久化 实现 普通的线段树通过懒标记(Lazy Tag)以 O ( n ...

  7. 蒟蒻的ACM数据结构(一)-线段树

    浅谈线段树的指针写法 一.基本概念 二.代码实现与基本操作 0.基础数据结构 1.建树 built函数 2. 单点查询 3.单点修改 4.区间查询 5.区间修改 三.优化 (一). Lazy-Tag懒 ...

  8. 0x43.数据结构进阶 - 线段树

    目录 一.基础线段树 线段树的建树 线段树的单点修改 线段树的区间查询 线段树的延迟标记(懒惰标记) 1.POJ3486 ASimpleProblemwithIntegersA\ Simple\ Pr ...

  9. 数据结构:线段树及ST算法比较

    ST算法是一种高效的计算区间最值的方法. 他的思想是将询问区间分解成两个最长的二次幂的长度的区间并集的形式. 所以与线段树不同,这种区间分解其实存在相交的分解. 因此ST算法能维护的只是一些简单的信息 ...

最新文章

  1. 深入掌握JMS(一):JMS基础
  2. Leangoo敏捷项目管理软件 6.3.2
  3. 步步为营-89-SQL语句(删除重复数据)
  4. 数据库面试题【十三、大表数据查询,怎么优化】
  5. CentOS 6.5高可用集群LVS+Keepalived
  6. 《网络风险及网络安全》培训总结
  7. C++中的error C2662,const的this指针问题
  8. 百度云网盘高速通道加速破解版 去广告纯净版干净云
  9. 遥感水文前景_【充电】学遥感必读的十本专业书
  10. 远程下载pdf文件 java代码
  11. 图像处理之前景检测(三)之码本(codebook)(主要为代码升级)
  12. 基于微信小程序会议室预约系统设计与实现毕业设计毕设开题报告参考
  13. 620集成显卡和mx250,独显有必要吗?英特尔UHD620核显对比MX150性能测试
  14. html tooltips效果,html5tooltips.js – 一款轻量级的3D工具提示插件
  15. 移动应用数据统计平台(之一)
  16. 电脑查服务器型号,怎么在服务器的操作系统中查看服务器的型号
  17. 挑战Man Group!顶级对冲基金的10道Python面试题
  18. 制作apt-get本地源解决无网络情况下安装软件
  19. asp.net 中Button按钮失效问题解决办法
  20. 题解 洛谷P1365 WJMZBMR打osu! / Easy

热门文章

  1. W ndoWs电脑上的线怎么接,win7一台电脑两个显示器的连接方法介绍
  2. Opencv中的erode和dilate(腐蚀和膨胀-python实现)
  3. mysql之第n高的薪水
  4. 高等数学笔记-乐经良老师-第五章-积分(Ⅰ)-定积分与不定积分-第一节-定积分的概念
  5. 【其它】visio画泳道图/跨职能流程图
  6. [重装系统系列]fcitx 小企鹅输入法 安装 in ubuntu 15.04
  7. Java重修之路(十)面向对象之多态详解,Object类,内部类,匿名内部类详解
  8. 偷偷赚钱的副业(真实有效)
  9. 往自己脖子以上的投资永远不会贬值
  10. ExtCertPathValidatorException: Could not validate certificate