[NOI2007]货币兑换Cash

/*

写完以后发现有牛人使用STL……在BZOJ(开了O2)上可以AC……

http://hi.baidu.com/wwwaaannngggrs/blog/item/e536b809c5b533d23bc763ca.html

*/


这道题是非常典型的斜率优化,蛋疼之处在于x并不单调,得用Splay维护……


方程是f[i]=max(f[i-1],a[i]*x[j]+b[i]*y[j])(j∈[1,n-1])//x[i],y[i]表示在f[i]取到最优值的情况下在第i天能够获得a券和b券的数量

暴力的O(n^2)算法是比较裸的//我一开始写了个暴力检验正确性……

但是极限数据为10w,O(n^2)过不了……


然后这道题可以斜率优化

把式子改写成

-a[i]*x[j]+f[i]=b[i]*y[j]

-a[i]/b[i]*x[j]+f[i]=y[j]

那么斜率为-a[i]/b[i],求最大截距

相当于维护一个上凸壳

问题在于这个式子的x,也就是x[i]是并不单调的,因为每天能够获得的a券数量不见得单调递增

那么要关于x建一棵Splay

然后每次找对于当前直线的最优值的时候

因为当前当前直线能够碰到的第一个点一定满足这样的性质:

这个点和它左边的点所在的直线斜率大于当前直线

这个点和它右边的点所在直线的斜率小于当前直线

那么Splay里面还维护一个这个点和它左边的点的斜率,和右边的店的斜率

就可以在logn时间里找出最优决策


插入的话可以找到应该插在那个位置,插入以后Splay到根

然后分别维护左边凸壳,右边凸壳 //具体做法在code中

/*

这道题十分诡异……我在本机利用cena和NOI07的官方数据测的时候是AC的,用时2.5s左右,加fastio用时1s以下

但是交到BZOJ和TW的一个OJ上,这两个以linux为平台的OJ会让我TLE掉,可以确定不是常数问题,加了fastio也是如此

也许是windows和linux又有哪里蛋疼了……因为电脑上没linux,就这样吧,以后有时间在linux下再看看

*/


//Lib #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<ctime> #include<iostream> #include<algorithm> #include<vector> #include<string> #include<queue> using namespace std; //Macro #define rep(i,a,b) for(int i=a,tt=b;i<=tt;++i) #define rrep(i,a,b) for(int i=a,tt=b;i>=tt;--i) #define erep(i,e,x) for(int i=x;i;i=e[i].next) #define irep(i,x) for(__typedef(x.begin()) i=x.begin();i!=x.end();i++) #define read() (strtol(ipos,&ipos,10)) #define sqr(x) ((x)*(x)) #define pb push_back #define PS system("pause"); typedef long long ll; typedef pair<int,int> pii; const int oo=~0U>>1; const double inf=1e20; const double eps=1e-6; string name="cash",in=".in",out=".out"; //Var int n,root;double s; double a[100008],b[100008],r[100008]; double f[100008]; long long base[20]; char Input[10000008],*ipos; struct T { int lc,rc,fa; double lk,rk,x,y; #define lc(t) tree[t].lc #define rc(t) tree[t].rc #define fa(t) tree[t].fa #define x(t) tree[t].x #define y(t) tree[t].y #define lk(t) tree[t].lk #define rk(t) tree[t].rk }tree[100008]; inline double getreal() { int x=0,z=0;char c; long long y=0; while (*ipos<=32) ipos++; while (1) { c=*ipos++; if (c<=32) return x; if (c<48) break; x=(x<<3)+(x<<1)+c-48; } while (1) { c=*ipos++; if (c<=32) return x+double(y)/base[z]; y=(y<<3)+(y<<1)+c-48; z++; } } void Zig(int x) { int y=fa(x),z=fa(y); if(lc(z)==y)lc(z)=x;else rc(z)=x;fa(x)=z; fa(rc(x))=y;lc(y)=rc(x);fa(y)=x;rc(x)=y; } void Zag(int x) { int y=fa(x),z=fa(y); if(lc(z)==y)lc(z)=x;else rc(z)=x;fa(x)=z; fa(lc(x))=y;rc(y)=lc(x);fa(y)=x;lc(x)=y; } void Splay(int x,int &goal) { int ff=fa(goal); for(int y,z;fa(x)!=ff;) { y=fa(x);z=fa(y); if(z==ff) if(lc(y)==x)Zig(x); else Zag(x); else if(lc(z)==y) if(lc(y)==x)Zig(y),Zig(x); else Zag(x),Zig(x); else if(rc(y)==x)Zag(y),Zag(x); else Zig(x),Zag(x); } goal=x; } double CalcY(int i){return f[i]/(r[i]*a[i]+b[i]);} int Find(double x) { for(int i=root;;) { if(lk(i)<x)i=lc(i); else if(rk(i)>x)i=rc(i); else return i; } } double CalcK(double xx1,double yy1,double xx2,double yy2){return (xx1==xx2)?-oo:(yy1-yy2)/(xx1-xx2);} int Merge(int x,int y) { int i=x; for(;rc(i);i=rc(i)); rc(i)=y;fa(y)=i; return x; } void Update() { int t=root; if(!lc(t)) { lk(t)=oo; rk(t)=lk(rc(t))=CalcK(x(t),y(t),x(rc(t)),y(rc(t))); } if(!rc(t)) { rk(t)=-oo; lk(t)=rk(lc(t))=CalcK(x(t),y(t),x(lc(t)),y(lc(t))); } if(lc(t)&&rc(t)) { lk(t)=rk(lc(t))=CalcK(x(t),y(t),x(lc(t)),y(lc(t))); rk(t)=lk(rc(t))=CalcK(x(t),y(t),x(rc(t)),y(rc(t))); if(lk(t)<=rk(t)) { root=Merge(lc(t),rc(t));fa(root)=0; rk(root)=lk(rc(root))=CalcK(x(root),y(root),x(rc(root)),y(rc(root))); } } } int FixL() { int s; for(int i=lc(root);i;) { if(CalcK(x(i),y(i),x(root),y(root))<lk(i))s=i,i=rc(i); else i=lc(i); } return s; } int FixR() { int s; for(int i=rc(root);i;) { if(CalcK(x(i),y(i),x(root),y(root))>rk(i))s=i,i=lc(i); else i=rc(i); } return s; } void Insert(int t) { y(t)=CalcY(t);x(t)=y(t)*r[t];int flag,i; for(i=root;i;) { if(x(i)>x(t)){flag=i;i=lc(i);} else if(x(i)<x(t)){flag=i;i=rc(i);} else if(y(i)>=y(t))return; else{y(i)=y(t),t=i;break;} } if(!i)if((x(flag)<x(t)))rc(flag)=t;else lc(flag)=t; if(!i)fa(t)=flag; Splay(t,root); if(lc(t)) { flag=FixL(); Splay(flag,lc(root)); rc(flag)=0; } if(rc(t)) { flag=FixR(); Splay(flag,rc(root)); lc(flag)=0; } Update(); } void Init() { base[0]=1; rep(i,1,18)base[i]=base[i-1]*10; fread(ipos=Input,10000000,1,stdin); n=read();s=getreal(); scanf("%d%lf",&n,&s); rep(i,1,n){a[i]=getreal();b[i]=getreal();r[i]=getreal();} } void Work() { f[1]=s;y(1)=CalcY(1);x(1)=y(1)*r[1];root=1;lk(1)=oo;rk(1)=-oo; rep(i,2,n) { int j=Find(-a[i]/b[i]); f[i]=max(f[i-1],x(j)*a[i]+y(j)*b[i]); Insert(i); } printf("%.3lf\n",f[n]); } int main() { // freopen((name+in).c_str(),"r",stdin); // freopen((name+out).c_str(),"w",stdout); Init(); Work(); return 0; }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值