[关闭]
@ivorysi 2018-01-02T09:02:27.000000Z 字数 24415 阅读 714

动态规划学习笔记

笔记


矩阵乘法

POJ 3070 Fibonacci

Description

In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn − 1 + Fn − 2 for n ≥ 2. For example, the first ten terms of the Fibonacci sequence are:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …
An alternative formula for the Fibonacci sequence is
Given an integer n, your goal is to compute the last 4 digits of Fn.

Input

The input test file will contain multiple test cases. Each test case consists of a single line containing n (where 0 ≤ n ≤ 1,000,000,000). The end-of-file is denoted by a single line containing the number −1.

Output

For each test case, print the last four digits of Fn. If the last four digits of Fn are all zeros, print ‘0’; otherwise, omit any leading zeros (i.e., print Fn mod 10000).

Sample Input

0
9
999999999
1000000000
-1

Sample Output

0
34
626
6875

题解

矩乘递推斐波那契第n项取模10000

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. //#define ivorysi
  10. using namespace std;
  11. typedef long long ll;
  12. struct matrix {
  13. int f[3][3];
  14. matrix() {
  15. memset(f,0,sizeof(f));
  16. }
  17. friend matrix operator *(const matrix &a,const matrix &b) {
  18. matrix c;
  19. siji(i,1,2) {
  20. siji(j,1,2) {
  21. siji(k,1,2) {
  22. c.f[i][j]+=a.f[i][k]*b.f[k][j]%10000;
  23. c.f[i][j]%=10000;
  24. }
  25. }
  26. }
  27. return c;
  28. }
  29. }a,ans;
  30. void fpow(int c) {
  31. matrix t=a;
  32. while(c) {
  33. if(c&1) ans=ans*t;
  34. t=t*t;
  35. c>>=1;
  36. }
  37. }
  38. int n;
  39. void solve() {
  40. if(n==0) {puts("0");return;}
  41. if(n==1 || n==2) {puts("1");return;}
  42. memset(ans.f,0,sizeof(ans.f));
  43. ans.f[1][1]=ans.f[2][2]=1;
  44. fpow(n-2);
  45. printf("%d\n",(ans.f[1][1]+ans.f[1][2])%10000);
  46. }
  47. int main() {
  48. #ifdef ivorysi
  49. freopen("f1.in","r",stdin);
  50. #endif
  51. a.f[1][1]=a.f[1][2]=a.f[2][1]=1;
  52. while(scanf("%d",&n)!=EOF) {
  53. if(n==-1) break;
  54. solve();
  55. }
  56. return 0;
  57. }

BZOJ 1009 1009: [HNOI2008]GT考试

Description

阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
0

Input

第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000

Output

阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100
111

Sample Output

81

题解

这道题表示到第i位匹配到第k个字符有多少种情况,要求k 转移方程可以考虑在i后面加一位匹配,看最多能匹配到哪
方程就是
表示加一位后从i匹配到j有几种情况
然后进行矩乘优化

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  7. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  8. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  9. //#define ivorysi
  10. using namespace std;
  11. typedef long long ll;
  12. int n,m,mod;
  13. char s[40];
  14. struct matrix {
  15. int f[25][25];
  16. matrix() {
  17. memset(f,0,sizeof(f));
  18. }
  19. friend matrix operator *(const matrix &a,const matrix &b) {
  20. matrix c;
  21. xiaosiji(i,0,m) {
  22. xiaosiji(j,0,m) {
  23. xiaosiji(k,0,m) {
  24. c.f[i][j]+=a.f[i][k]*b.f[k][j]%mod;
  25. c.f[i][j]%=mod;
  26. }
  27. }
  28. }
  29. return c;
  30. }
  31. }a,ans;
  32. void fpow(int c) {
  33. matrix t=a;
  34. while(c) {
  35. if(c&1) ans=ans*t;
  36. t=t*t;
  37. c>>=1;
  38. }
  39. }
  40. int nxt[25];
  41. int main() {
  42. #ifdef ivorysi
  43. freopen("f1.in","r",stdin);
  44. #endif
  45. scanf("%d%d%d",&n,&m,&mod);
  46. scanf("%s",s+1);
  47. siji(i,2,m) {
  48. int p=nxt[i-1];
  49. while(p && s[p+1]!=s[i]) p=nxt[p];
  50. if(s[p+1]==s[i]) nxt[i]=p+1;
  51. else nxt[i]=0;
  52. }
  53. xiaosiji(i,0,m) {
  54. siji(j,0,9) {
  55. if(s[i+1]=='0'+j) {
  56. if(i+1==m) continue;
  57. ++a.f[i+1][i];continue;
  58. }
  59. int k=nxt[i];
  60. while(k && s[k+1]!=j+'0') k=nxt[k];
  61. if(s[k+1]=='0'+j) ++a.f[k+1][i];
  62. else ++a.f[0][i];
  63. }
  64. }
  65. xiaosiji(i,0,m) ans.f[i][i]=1;
  66. fpow(n);
  67. int res=0;
  68. xiaosiji(i,0,m) {
  69. res+=ans.f[i][0];
  70. res%=mod;
  71. }
  72. printf("%d\n",res);
  73. return 0;
  74. }

BZOJ 3329: Xorequ

Description

Input

第一行一个正整数,表示数据组数据 ,接下来T行
每行一个正整数N

Output

2*T行
第2*i-1行表示第i个数据中问题一的解,
第2*i行表示第i个数据中问题二的解,

Sample Input

1
1

Sample Output

1
2

HINT

x=1与x=2都是原方程的根,注意第一个问题的解
不要mod 10^9+7
1<=N<=10^18
1<=T<=1000

题解

我们要注意到这样一个结论
a^b=c可以得到a^c=b
证明可以是a^b^b^c=c^b^c
a^c=b
然后我们发现x^2x=3x
2x是把x整体左移,那么我们可以得知这样的x是没有两个连续的1,才能让异或变成加法
这样的数可以用数位dp解决
递推方程是
dp[i][0]=dp[i-1][0]+dp[i-1][1]
dp[i][1]=dp[i-1][0]
dp[i][1/0]表示长度为i的二进制数最高位是1或者是0
然后这个递推方程的边界是dp[1][0]=dp[1][1]=1
写成递推矩阵的话和斐波那契的矩阵其实是一样的
第二问可以这么考虑的答案,是dp[n+1][0],去掉0的情况再加上的情况,答案就是dp[n+1][0]用矩阵递推出来即可

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #include <set>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define mod 1000000007
  11. //#define ivorysi
  12. using namespace std;
  13. typedef long long ll;
  14. struct matrix {
  15. ll f[3][3];
  16. matrix() {
  17. memset(f,0,sizeof(f));
  18. }
  19. friend matrix operator * (const matrix &a,const matrix &b) {
  20. matrix c;
  21. siji(i,1,2) {
  22. siji(j,1,2) {
  23. siji(k,1,2) {
  24. c.f[i][j]+=a.f[i][k]*b.f[k][j]%mod;
  25. c.f[i][j]%=mod;
  26. }
  27. }
  28. }
  29. return c;
  30. }
  31. }a,tar;
  32. ll n;
  33. ll dp[70][2];
  34. void fpow(ll c) {
  35. matrix t=a;
  36. while(c) {
  37. if(c&1) tar=tar*t;
  38. t=t*t;
  39. c>>=1;
  40. }
  41. }
  42. void solve() {
  43. scanf("%lld",&n);
  44. ll ans=0;
  45. bool content=false;
  46. gongzi(i,61,0) {
  47. ll k=(ll)1<<i;
  48. if(n&k) {
  49. if(!content) ans+=dp[i+1][0];
  50. ll t=(ll)1<<(i+1);
  51. if(n&t) content=true;
  52. }
  53. }
  54. if((n^(2*n))==(3*n)) ++ans;
  55. --ans;
  56. printf("%lld\n",ans);
  57. memset(tar.f,0,sizeof(tar.f));
  58. tar.f[1][1]=tar.f[2][2]=1;
  59. fpow(n);
  60. printf("%lld\n",(tar.f[1][1]+tar.f[1][2])%mod);
  61. }
  62. int main() {
  63. #ifdef ivorysi
  64. freopen("f1.in","r",stdin);
  65. #endif
  66. int T;
  67. scanf("%d",&T);
  68. a.f[1][1]=a.f[1][2]=a.f[2][1]=1;
  69. dp[1][0]=dp[1][1]=1;
  70. siji(i,2,65) {
  71. dp[i][1]=dp[i-1][0];
  72. dp[i][0]=dp[i-1][1]+dp[i-1][0];
  73. }
  74. while(T--) {
  75. solve();
  76. }
  77. return 0;
  78. }

BZOJ 1297: [SCOI2009]迷路

Description

windy在有向图中迷路了。 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1。 现在给出该有向图,你能告诉windy总共有多少种不同的路径吗? 注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

Input

第一行包含两个整数,N T。 接下来有 N 行,每行一个长度为 N 的字符串。 第i行第j列为'0'表示从节点i到节点j没有边。 为'1'到'9'表示从节点i到节点j需要耗费的时间。

Output

包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以2009的余数。

Sample Input

【输入样例一】

2 2
11
00

【输入样例二】

5 30
12045
07105
47805
12024
12345

Sample Output

【输出样例一】
1
【样例解释一】
0->0->1
【输出样例二】
852

HINT

30%的数据,满足 2 <= N <= 5 ; 1 <= T <= 30 。 100%的数据,满足 2 <= N <= 10 ; 1 <= T <= 1000000000 。

题解

我的做法比较愚蠢,因为可以把一个点拆成很多10个点,这样就可以用邻接矩阵递推
这种做法上一个时间向下一个时间连一条边,然后条边按照边权从i的k时间点向j连一条边
还有一种
dp[u][t]表示到点u时间为t的方案数
矩阵是显然的,100个点递推即可(然而把9写成8的我真的是要蠢死了……)
初始化的时候还要把前9个时间递推一下
然而这道题说是T恰好到n-1,它还有可能中途到了n-1然后在晃悠几圈,很迷

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <cstring>
  6. #include <set>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define mod 1000000007
  11. //#define ivorysi
  12. using namespace std;
  13. typedef long long ll;
  14. int n;
  15. struct matrix {
  16. int f[105][105];
  17. matrix() {
  18. memset(f,0,sizeof(f));
  19. }
  20. friend matrix operator * (const matrix &a,const matrix &b) {
  21. matrix c;
  22. siji(i,1,100) {
  23. siji(j,1,100) {
  24. siji(k,1,100) {
  25. c.f[i][j]+=a.f[i][k]*b.f[k][j]%2009;
  26. c.f[i][j]%=2009;
  27. }
  28. }
  29. }
  30. return c;
  31. }
  32. }a,tar;
  33. char s[15][15];
  34. int T,dp[15][15],l[105],tot;
  35. void fpow(int c) {
  36. matrix t=a;
  37. while(c) {
  38. if(c&1) tar=tar*t;
  39. t=t*t;
  40. c>>=1;
  41. }
  42. }
  43. int main() {
  44. #ifdef ivorysi
  45. freopen("f1.in","r",stdin);
  46. #endif
  47. scanf("%d%d",&n,&T);
  48. siji(i,1,n) scanf("%s",s[i]+1);
  49. siji(i,1,n) {
  50. siji(j,1,n) {
  51. if(s[i][j]-'0'!=0) {
  52. int k=s[i][j]-'0';
  53. a.f[j*10][i*10-k+1]=1;
  54. }
  55. }
  56. siji(j,1,9) a.f[(i-1)*10+j][(i-1)*10+j+1]=1;//虽然是0-8但是9个点orz
  57. }
  58. dp[1][0]=1;
  59. siji(h,1,9) {
  60. siji(i,1,n) {
  61. siji(j,1,n) {
  62. int k=s[i][j]-'0';
  63. if(k==0) continue;
  64. if(h<k) continue;
  65. dp[j][h]=(dp[j][h]+dp[i][h-k])%2009;
  66. }
  67. }
  68. }
  69. if(T<=9) {printf("%d\n",dp[n][T]);return 0;}
  70. siji(i,1,n) {
  71. siji(j,0,9) {
  72. l[++tot]=dp[i][j];
  73. }
  74. }
  75. siji(i,1,n*10)tar.f[i][i]=1;
  76. fpow(T-10);
  77. int ans=0;
  78. siji(i,1,n) {
  79. int k=s[i][n]-'0';
  80. if(k==0) continue;
  81. int t=i*10-k+1;
  82. siji(j,1,tot) ans+=l[j]*tar.f[t][j]%2009,ans%=2009;
  83. }
  84. printf("%d\n",ans);
  85. return 0;
  86. }

模型1

我们对于一个这样的式子

是有决策单调性的
什么是决策单调性呢,加入我们用x更新的i,y更新的j,在i < j的情况下 x <= y
例如我们一开始只有f[0]这个决策
我们对于决策的更新就是

  1. 1111111111111111111111111

然后又有了一个新决策,决策的更新方式是这样的

  1. 1111111111222222222222222

之后再来一个决策

  1. 1111111111222222223333333

或者是

  1. 1111113333333333333333333

而不可能是

  1. 1111111111333332222222222

所以我们用一个队列,每次拿新决策和队尾的老决策进行比较,如果在老决策的起点处新决策最优,就全部舍弃老决策
否则老决策中肯定有一个转折点,我们可以二分找到这个转折点

BZOJ 1597: [Usaco2008 Mar]土地购买

Description

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.

Input

  • 第1行: 一个数: N
  • 第2..N+1行: 第i+1行包含两个数,分别为第i块土地的长和宽

Output

  • 第一行: 最小的可行费用.

Sample Input

4
100 1
15 15
20 5
1 100

输入解释:

共有4块土地.

Sample Output

500

HINT

FJ分3组买这些土地: 第一组:100x1, 第二组1x100, 第三组20x5 和 15x15 plot. 每组的价格分别为100,100,300, 总共500.

题解

这道题可能看起来没有决策单调性,但是我们把所有点按照长度排序后,我们后面的决策只和宽度有关

这似乎不是那个模型,可是把这些点画在一个二维坐标里就会发现
有的土地长宽都被另一块土地覆盖了,则可以舍弃不考虑
这些点扫一遍就可以求出来,然后对于剩下的点,我们就有

这就满足那个式子了,可以直接应用上述的决策单调性

代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <vector>
  6. #include <stack>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define MAXN 50005
  11. #define mod 1000000007
  12. //#define ivorysi
  13. using namespace std;
  14. typedef long long ll;
  15. ll read() {
  16. ll res=0,neg=1;
  17. char c=getchar();
  18. while(c<'0' || c>'9') {
  19. if(c=='-') neg=-1;
  20. c=getchar();
  21. }
  22. while(c>='0' && c<='9') {
  23. res=res*10+c-'0';
  24. c=getchar();
  25. }
  26. return res*neg;
  27. }
  28. struct node {
  29. ll len,wid;
  30. bool operator < (const node &b) const{
  31. return len>b.len || (len==b.len && wid>b.wid);
  32. }
  33. }tmp[MAXN],area[MAXN];
  34. int n,tot,que[MAXN][3],t,h;
  35. ll f[MAXN];
  36. ll calc(int l,int z) {
  37. return f[l]+area[l+1].len*area[z].wid;
  38. }
  39. void solve() {
  40. n=read();
  41. siji(i,1,n) {
  42. tmp[i].len=read();
  43. tmp[i].wid=read();
  44. }
  45. sort(tmp+1,tmp+n+1);
  46. ll maxwid=0;
  47. siji(i,1,n) {
  48. if(tmp[i].wid>maxwid) {
  49. area[++tot]=tmp[i];
  50. }
  51. maxwid=max(tmp[i].wid,maxwid);
  52. }
  53. h=t=1;
  54. que[1][0]=0,que[1][1]=0,que[1][2]=tot;
  55. siji(i,1,tot) {
  56. if(que[h][1]<que[h][2]) ++que[h][1];
  57. else ++h;
  58. f[i]=calc(que[h][0],i);
  59. if(calc(i,tot)>=calc(que[t][0],tot)) continue;
  60. while(h<=t && calc(que[t][0],que[t][1])>=calc(i,que[t][1])) --t;
  61. if(h>t) {
  62. que[++t][0]=i,que[t][1]=i,que[t][2]=tot;
  63. }
  64. else {
  65. int l=que[t][1],r=que[t][2];
  66. while(l<r) {
  67. int m=(l+r+1)>>1;
  68. if(calc(que[t][0],m)<calc(i,m)) {
  69. l=m;
  70. }
  71. else {
  72. r=m-1;
  73. }
  74. }
  75. que[t][2]=l;
  76. que[++t][0]=i,que[t][1]=l+1,que[t][2]=tot;
  77. }
  78. }
  79. printf("%lld\n",f[tot]);
  80. }
  81. int main() {
  82. #ifdef ivorysi
  83. freopen("f1.in","r",stdin);
  84. #endif
  85. solve();
  86. }

BZOJ 1096: [ZJOI2007]仓库建设

Description

L公司有N个工厂,由高到底分布在一座山上。如图所示,工厂1在山顶,工厂N在山脚。由于这座山处于高原内
陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L公司的总裁L先生接到气象
部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。由于
地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库
的费用是Ci。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设
置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,
假设一件产品运送1个单位距离的费用是1。假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到
以下数据:1:工厂i距离工厂1的距离Xi(其中X1=0);2:工厂i目前已有成品数量Pi;:3:在工厂i建立仓库的费用
Ci;请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。

Input

第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。

Output

仅包含一个整数,为可以找到最优方案的费用。

Sample Input

3
0 5 10
5 3 100
9 6 10

Sample Output

32

HINT

在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。

【数据规模】

对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。

题解

这道题显然是有决策单调性的,因为我们dp[k]表示位置为k的地方建仓库使得1-k能够全部有仓库存放的总花费
我们从某个i转移,显然i越大,转移到k的花费就越小,然后考虑决策单调性,打个表然后看看是不是……然后愉快的套算法

然后拆式子


然后那两个式子就可以通过前缀和来维护了,这道题就做完了

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <vector>
  6. #include <stack>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define MAXN 1000005
  11. #define mod 1000000007
  12. //#define ivorysi
  13. using namespace std;
  14. typedef long long ll;
  15. ll read() {
  16. ll res=0,neg=1;
  17. char c=getchar();
  18. while(c<'0' || c>'9') {
  19. if(c=='-') neg=-1;
  20. c=getchar();
  21. }
  22. while(c>='0' && c<='9') {
  23. res=res*10+c-'0';
  24. c=getchar();
  25. }
  26. return res*neg;
  27. }
  28. int n;
  29. ll dis[MAXN],num[MAXN],gar[MAXN];
  30. ll sum1[MAXN],sum2[MAXN],dp[MAXN];//sum1 num sum2 dis*num
  31. int que[MAXN][3],qh,qt;
  32. ll calc(int l,int z) {
  33. return dp[l]+dis[z]*(sum1[z-1]-sum1[l])-(sum2[z-1]-sum2[l])+gar[z];
  34. }
  35. void solve() {
  36. n=read();
  37. siji(i,1,n) {
  38. dis[i]=read();
  39. num[i]=read();
  40. gar[i]=read();
  41. }
  42. siji(i,1,n) {
  43. sum1[i]=num[i]+sum1[i-1];
  44. sum2[i]=dis[i]*num[i]+sum2[i-1];
  45. }
  46. qh=qt=1;
  47. que[1][0]=0;que[1][1]=0;que[1][2]=n;
  48. siji(i,1,n) {
  49. if(que[qh][1]<que[qh][2]) ++que[qh][1];
  50. else ++qh;
  51. dp[i]=calc(que[qh][0],i);
  52. if(calc(que[qt][0],n)<=calc(i,n)) continue;
  53. while(qh<=qt && calc(que[qt][0],que[qt][1])>=calc(i,que[qt][1])) --qt;
  54. if(qh>qt) {
  55. que[++qt][0]=i;que[qt][1]=i;que[qt][2]=n;
  56. }
  57. else {
  58. int l=que[qt][1],r=que[qt][2];
  59. while(l<r) {
  60. int m=(l+r+1)>>1;
  61. if(calc(que[qt][0],m)<=calc(i,m)) {
  62. l=m;//这里都打成l=i了
  63. }
  64. else {
  65. r=m-1;
  66. }
  67. }
  68. que[qt][2]=l;
  69. que[++qt][0]=i;que[qt][1]=l+1;que[qt][2]=n;
  70. }
  71. }
  72. printf("%lld\n",dp[n]);
  73. }
  74. int main() {
  75. #ifdef ivorysi
  76. freopen("f1.in","r",stdin);
  77. #endif
  78. solve();
  79. }

模型2

还有这样一个式子
其中b[x]随x不降
这样的话我们可以注意到,如果, 那么j必然无用,可以舍去
这让我们考虑到单调队列,我们可以用单调队列来维护这个东西,让队首控制在合法的范围内,然后保持整个队列单调递增,每次用队首来更新即可

经典例题

多重背包问题

二进制优化可以使得多重背包的复杂度降到log级别,但是还有更好的O(NP)做法,N是物品个数,P是背包体积

现在我们有N种物品
每种物品有一个价值val[i],一个体积weight[i],还有一个数目num[i]
现在给你一个体积为P的背包,求最大价值

题解

对于一个体积为i的背包f[i],当前枚举到背包的种类是x,我们可以从, ....转移过来
然后我们可以写出方程

然而这个方程……在i移动的时候(i-k×weight[x])都会变化,不满足模型啊……怎么办呢……
我们发现,对于一个固定的物品,每段体积的转移都是%weight[x]相等的,所以我们可以选择从weight[x]的剩余系开始枚举,逐个加倍
然后再观察这个式子,考虑变形


设t-k=z

这样我们发现,和k并没有什么关系,我们把队首的范围控制一下,就好了
这个问题就被优化成O(NP)了

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <set>
  8. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  9. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  10. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  11. //#define ivorysi
  12. #define MAXN 105
  13. #define ha 9779797
  14. using namespace std;
  15. typedef long long ll;
  16. ll dp[50005];
  17. int n,w;
  18. int v[105],p[105],c[105];
  19. int q[50005],head,tail,poi;
  20. int main() {
  21. #ifdef ivorysi
  22. freopen("f1.in","r",stdin);
  23. #endif
  24. scanf("%d%d",&n,&w);
  25. siji(i,1,n) {
  26. scanf("%d%d%d",&v[i],&p[i],&c[i]);
  27. }
  28. siji(i,1,n) {
  29. xiaosiji(j,0,v[i]) {
  30. int k=(w-j)/v[i];
  31. head=tail=1;
  32. poi=k-1;
  33. gongzi(l,k,1) {
  34. for(;poi>=(l-c[i]) && poi>=0;--poi) {
  35. while(tail>1 &&
  36. dp[q[tail-1]*v[i]+j]-q[tail-1]*p[i]<dp[poi*v[i]+j]-poi*p[i]) --tail;
  37. q[tail++]=poi;
  38. }
  39. if(q[head]==l) ++head;
  40. //if(head>=tail) continue;
  41. dp[l*v[i]+j]=max(dp[l*v[i]+j],dp[q[head]*v[i]+j]-q[head]*p[i]+l*p[i]);
  42. }
  43. }
  44. }
  45. printf("%lld\n",dp[w]);
  46. }

BZOJ 1499: [NOI2005]瑰丽华尔兹

你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意?众所周知,跳华尔兹时,最重要的是有好的音乐。但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上,他的名字叫丹尼•布德曼•T.D.•柠檬•1900,朋友们都叫他1900。 1900在20世纪的第一年出生在往返于欧美的邮轮弗吉尼亚号上,很不幸他刚出生就被抛弃了,成了孤儿。1900孤独的成长在弗吉尼亚号上,从未离开过这个摇晃的世界。也许是对他命运的补偿,上帝派可爱的小天使艾米丽照顾他。可能是天使的点化,1900拥有不可思议的钢琴天赋:从未有人教,从没看过乐谱,但他却能凭着自己的感觉弹出最沁人心脾的旋律。当1900的音乐获得邮轮上所有人的欢迎时,他才8岁,而此时的他已经乘着海轮往返欧美大陆50余次了。虽说是钢琴奇才,但1900还是个孩子,他有着和一般男孩一样的好奇和调皮,只不过更多一层浪漫的色彩罢了:这是一个风雨交加的夜晚,海风卷起层层巨浪拍打着弗吉尼亚号,邮轮随着巨浪剧烈的摇摆。船上的新萨克斯手马克斯•托尼晕船了,1900招呼托尼和他一起坐上舞厅里的钢琴,然后松开了固定钢琴的闸,于是,钢琴随着海轮的倾斜滑动起来。准确的说,我们的主角1900、钢琴、邮轮随着1900的旋律一起跳起了华尔兹,随着“嘣嚓嚓”的节奏,托尼的晕船症也奇迹般的消失了。后来托尼在回忆录上写道:大海摇晃着我们使我们转来转去快速的掠过灯和家具我意识到我们正在和大海一起跳舞真是完美而疯狂的舞者晚上在金色的地板上快乐的跳着华尔兹是不是很惬意呢?也许,我们忘记了一个人,那就是艾米丽,她可没闲着:她必须在适当的时候施展魔法帮助1900,不让钢琴碰上舞厅里的家具。不妨认为舞厅是一个N行M列的矩阵,矩阵中的某些方格上堆放了一些家具,其他的则是空地。钢琴可以在空地上滑动,但不能撞上家具或滑出舞厅,否则会损坏钢琴和家具,引来难缠的船长。每个时刻,钢琴都会随着船体倾斜的方向向相邻的方格滑动一格,相邻的方格可以是向东、向西、向南或向北的。而艾米丽可以选择施魔法或不施魔法:如果不施魔法,则钢琴会滑动;如果施魔法,则钢琴会原地不动。艾米丽是个天使,她知道每段时间的船体的倾斜情况。她想使钢琴在舞厅里滑行路程尽量长,这样1900会非常高兴,同时也有利于治疗托尼的晕船。但艾米丽还太小,不会算,所以希望你能帮助她。

Input

输入文件的第一行包含5个数N, M, x, y和K。N和M描述舞厅的大小,x和y为钢琴的初始位置(x行y列);我们对船体倾斜情况是按时间的区间来描述的,且从1开始计量时间,比如“在[1, 3]时间里向东倾斜,[4, 5]时间里向北倾斜”,因此这里的K表示区间的数目。以下N行,每行M个字符,描述舞厅里的家具。第i行第j列的字符若为‘ . ’,则表示该位置是空地;若为‘ x ’,则表示有家具。以下K行,顺序描述K个时间区间,格式为:si ti di。表示在时间区间[si, ti]内,船体都是向di方向倾斜的。di为1, 2, 3, 4中的一个,依次表示北、南、西、东(分别对应矩阵中的上、下、左、右)。输入保证区间是连续的,即 s1 = 1 si = ti-1 + 1 (1 < i ≤ K) tK = T

Output

输出文件仅有1行,包含一个整数,表示钢琴滑行的最长距离(即格子数)。

Sample Input

4 5 4 1 3
..xx.
.....
...x.
.....
1 3 4
4 5 1
6 7 3

Sample Output

6

题解

复杂度是
单调队列优化一发……对于四个方向采取不同的维护,再枚举每个点进行更新是

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <vector>
  6. #include <stack>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define MAXN 1000005
  11. #define mod 1000000007
  12. using namespace std;
  13. typedef long long ll;
  14. ll read() {
  15. ll res=0,neg=1;
  16. char c=getchar();
  17. while(c<'0' || c>'9') {
  18. if(c=='-') neg=-1;
  19. c=getchar();
  20. }
  21. while(c>='0' && c<='9') {
  22. res=res*10+c-'0';
  23. c=getchar();
  24. }
  25. return res*neg;
  26. }
  27. int n,m,x,y,k;
  28. int s,t,d;
  29. char map[305][305];
  30. int dp[2][305][305],now,que[305],hd,tl;
  31. void calc1() {
  32. int delta=t-s+1;
  33. siji(j,1,m) {
  34. hd=1,tl=0;
  35. que[1]=0;
  36. gongzi(i,n,1) {
  37. if(map[i][j]=='x') {hd=1,tl=0;que[1]=0;continue;}
  38. while(hd<=tl && que[hd]>i+delta) ++hd;
  39. if(hd<=tl && dp[now][que[hd]][j]!=0) {
  40. dp[now^1][i][j]=dp[now][que[hd]][j]+que[hd]-i;
  41. }
  42. dp[now^1][i][j]=max(dp[now][i][j],dp[now^1][i][j]);
  43. if(dp[now][i][j]==0) continue;
  44. while(hd<=tl && dp[now][que[tl]][j]+que[tl]<dp[now][i][j]+i) --tl;
  45. que[++tl]=i;
  46. }
  47. }
  48. }
  49. void calc2() {
  50. int delta=t-s+1;
  51. siji(j,1,m) {
  52. hd=1,tl=0;
  53. que[1]=0;
  54. siji(i,1,n) {
  55. if(map[i][j]=='x') {hd=1,tl=0;que[1]=0;continue;}
  56. while(hd<=tl && que[hd]<i-delta) ++hd;
  57. if(hd<=tl && dp[now][que[hd]][j]!=0) {
  58. dp[now^1][i][j]=dp[now][que[hd]][j]+i-que[hd];
  59. }
  60. dp[now^1][i][j]=max(dp[now][i][j],dp[now^1][i][j]);
  61. if(dp[now][i][j]==0) continue;
  62. while(hd<=tl && dp[now][que[tl]][j]-que[tl]<dp[now][i][j]-i) --tl;
  63. que[++tl]=i;
  64. }
  65. }
  66. }
  67. void calc3() {
  68. int delta=t-s+1;
  69. siji(i,1,n) {
  70. hd=1,tl=0;
  71. que[1]=0;
  72. gongzi(j,m,1) {
  73. if(map[i][j]=='x') {hd=1,tl=0;que[1]=0;continue;}
  74. while(hd<=tl && que[hd]>j+delta) ++hd;
  75. if(hd<=tl && dp[now][i][que[hd]]!=0) {
  76. dp[now^1][i][j]=dp[now][i][que[hd]]+que[hd]-j;
  77. }
  78. dp[now^1][i][j]=max(dp[now][i][j],dp[now^1][i][j]);
  79. if(dp[now][i][j]==0) continue;
  80. while(hd<=tl && dp[now][i][que[tl]]+que[tl]<dp[now][i][j]+j)--tl;
  81. que[++tl]=j;
  82. }
  83. }
  84. }
  85. void calc4() {
  86. int delta=t-s+1;
  87. siji(i,1,n) {
  88. hd=1;tl=0;
  89. que[1]=0;
  90. siji(j,1,m) {
  91. if(map[i][j]=='x') {hd=1,tl=0;que[1]=0;continue;}
  92. while(hd<=tl && que[hd]<j-delta) ++hd;
  93. if(hd<=tl && dp[now][i][que[hd]]!=0) {
  94. dp[now^1][i][j]=dp[now][i][que[hd]]+j-que[hd];
  95. }
  96. dp[now^1][i][j]=max(dp[now][i][j],dp[now^1][i][j]);
  97. if(dp[now][i][j]==0) continue;
  98. while(hd<=tl && dp[now][i][que[tl]]-que[tl]<dp[now][i][j]-j)--tl;
  99. que[++tl]=j;
  100. }
  101. }
  102. }
  103. void solve() {
  104. n=read();m=read();x=read();y=read();k=read();
  105. dp[now][x][y]=1;
  106. siji(i,1,n) {
  107. scanf("%s",map[i]+1);
  108. }
  109. if(map[x][y]=='x') dp[now][x][y]=0;
  110. siji(i,1,k) {
  111. s=read();t=read();d=read();
  112. if(d==1) calc1();
  113. else if(d==2) calc2();
  114. else if(d==3) calc3();
  115. else calc4();
  116. now^=1;
  117. }
  118. int ans=0;
  119. siji(i,1,n) {
  120. siji(j,1,m) {
  121. ans=max(ans,dp[now][i][j]);
  122. }
  123. }
  124. --ans;
  125. if(ans<0) ans=0;
  126. printf("%d",ans);
  127. }
  128. int main() {
  129. solve();
  130. }

模型3


作为横轴,作为纵轴


我们发现,最优决策一定在平面内的一个凸包上,然后相当于一个斜率一定的直线自负无穷向上平移,碰到的一个数据点即为答案
然后如果直线的斜率和二元组的x坐标同时满足单调性,那么我们可以维护一个凸壳,然后用一个单调队列维护凸壳上的点,我们从队列前往后找一个最优的的点,因为前面的点斜率一定不优,而斜率单调变化,前面舍弃的点一定没有用了,然后我们用叉乘维护这个凸壳下凸就可以了
其实在代码实现里,这个直线是不用存在的,因为从前往后去计算决策的那个式子,找一个最优解,就相当于比较斜率了

BZOJ 1010: [HNOI2008]玩具装箱toy

Description

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小.

Input

第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7

Output

输出最小费用

Sample Input

5 4
3
4
2
1
4

Sample Output

1

题解

sum表示前缀和

然后分个类




作为y轴
作为x轴
斜率单调增,x轴左边单调增,维护一个下凸壳,取最小值

代码

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <vector>
  6. #include <stack>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define MAXN 50005
  11. #define mod 1000000007
  12. //#define ivorysi
  13. using namespace std;
  14. typedef long long ll;
  15. ll read() {
  16. ll res=0,neg=1;
  17. char c=getchar();
  18. while(c<'0' || c>'9') {
  19. if(c=='-') neg=-1;
  20. c=getchar();
  21. }
  22. while(c>='0' && c<='9') {
  23. res=res*10+c-'0';
  24. c=getchar();
  25. }
  26. return res*neg;
  27. }
  28. int n;
  29. ll L,c[MAXN];
  30. ll a[MAXN],b[MAXN],sum[MAXN],X[MAXN],Y[MAXN],f[MAXN];
  31. int que[MAXN],qh,qt;
  32. ll calc(int l,int z) {
  33. return f[l]-2*a[z]*b[l]+b[l]*b[l];
  34. }
  35. bool slope(int l,int z,int s) {
  36. return (X[l]-X[s])*(Y[z]-Y[s])-(X[z]-X[s])*(Y[l]-Y[s])>=0;
  37. }
  38. void solve() {
  39. n=read();
  40. L=read();
  41. siji(i,1,n) {
  42. c[i]=read();
  43. sum[i]=sum[i-1]+c[i];
  44. }
  45. siji(i,1,n) {
  46. b[i]=sum[i]+i;
  47. a[i]=b[i]-L-1;
  48. }
  49. qh=qt=1;que[1]=0;
  50. siji(i,1,n) {
  51. while(qh<qt && calc(que[qh],i)>=calc(que[qh+1],i)) ++qh;
  52. f[i]=calc(que[qh],i)+a[i]*a[i];
  53. X[i]=b[i];Y[i]=f[i]+b[i]*b[i];
  54. while(qh<qt && slope(que[qt],que[qt-1],i)) --qt;
  55. que[++qt]=i;
  56. }
  57. printf("%lld\n",f[n]);
  58. }
  59. int main() {
  60. #ifdef ivorysi
  61. freopen("f1.in","r",stdin);
  62. #endif
  63. solve();
  64. }

BZOJ 1911: [Apio2010]特别行动队

给出一个数N表示一共有N个士兵,每个士兵有一个战斗力
然后将士兵分成任意多个连续区间,每个区间的值是然后求这些值的和最大是多少

输入

第一行给出一个N
第二行给出a,b,c
然后给出N个数,是每个士兵的战斗力

输出

战斗力最大的总和

题解

推式子


然后拿作为y轴,作为x轴
横坐标单调增,斜率单调减
由于要取最大值,所以我们需要维护一个上凸的凸包

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <vector>
  5. #include <cstring>
  6. #include <cstdlib>
  7. #include <set>
  8. #include <queue>
  9. #include <map>
  10. #define siji(i,x,y) for(int i=(x);i<=(y);i++)
  11. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  12. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  13. #define pii pair<int,int>
  14. #define fi first
  15. #define se second
  16. //#define ivorysi
  17. #define MAXN 1000005
  18. #define mod 1000000007
  19. using namespace std;
  20. typedef long long ll;
  21. ll read() {
  22. ll res=0,neg=1;
  23. char c=getchar();
  24. while(c<'0'||c>'9') {
  25. if(c=='-')neg=-1;
  26. c=getchar();
  27. }
  28. while(c>='0'&&c<= '9') {
  29. res=res*10+c-'0';
  30. c=getchar();
  31. }
  32. return res*neg;
  33. }
  34. void out(ll x) {
  35. if(x<0) {putchar('-');x=-x;}
  36. if(x>=10) {
  37. out(x/10);
  38. }
  39. putchar('0'+x%10);
  40. }
  41. int que[MAXN],hd,tl;
  42. int n;
  43. ll val[MAXN],dp[MAXN],sum[MAXN],X[MAXN],Y[MAXN],a,b,c;
  44. ll calc(int l,int z) {
  45. return dp[l]+a*sum[l]*sum[l]-2*a*sum[z]*sum[l]-b*sum[l];
  46. }
  47. bool slope(int l,int z,int s) {
  48. return (X[l]-X[s])*(Y[z]-Y[s])-(X[z]-X[s])*(Y[l]-Y[s])>=0;
  49. }
  50. void solve() {
  51. n=read();
  52. a=read();b=read();c=read();
  53. siji(i,1,n) {
  54. val[i]=read();
  55. sum[i]=sum[i-1]+val[i];
  56. }
  57. que[hd=tl=1]=0;
  58. siji(i,1,n) {
  59. while(hd<tl && calc(que[hd],i)<=calc(que[hd+1],i)) ++hd;
  60. dp[i]=calc(que[hd],i)+a*sum[i]*sum[i]+b*sum[i]+c;
  61. X[i]=sum[i];
  62. Y[i]=dp[i]+a*sum[i]*sum[i]-b*sum[i];
  63. while(hd<tl && slope(que[tl],i,que[tl-1])) --tl;
  64. que[++tl]=i;
  65. }
  66. printf("%lld\n",dp[n]);
  67. }
  68. int main() {
  69. #ifdef ivorysi
  70. freopen("f1.in","r",stdin);
  71. #endif
  72. solve();
  73. }

BZOJ 1492: [NOI2007]货币兑换Cash

Description

小Y最近在一家金券交易所工作。该金券交易所只发行交易两种金券:A纪念券(以下简称A券)和 B纪念券(以下
简称B券)。每个持有金券的顾客都有一个自己的帐户。金券的数目可以是一个实数。每天随着市场的起伏波动,
两种金券都有自己当时的价值,即每一单位金券当天可以兑换的人民币数目。我们记录第 K 天中 A券 和 B券 的
价值分别为 AK 和 BK(元/单位金券)。为了方便顾客,金券交易所提供了一种非常方便的交易方式:比例交易法
。比例交易法分为两个方面:(a)卖出金券:顾客提供一个 [0,100] 内的实数 OP 作为卖出比例,其意义为:将
OP% 的 A券和 OP% 的 B券 以当时的价值兑换为人民币;(b)买入金券:顾客支付 IP 元人民币,交易所将会兑
换给用户总价值为 IP 的金券,并且,满足提供给顾客的A券和B券的比例在第 K 天恰好为 RateK;例如,假定接
下来 3 天内的 Ak、Bk、RateK 的变化分别为:
假定在第一天时,用户手中有 100元 人民币但是没有任何金券。用户可以执行以下的操作:
注意到,同一天内可以进行多次操作。小Y是一个很有经济头脑的员工,通过较长时间的运作和行情测算,他已经
知道了未来N天内的A券和B券的价值以及Rate。他还希望能够计算出来,如果开始时拥有S元钱,那么N天后最多能
够获得多少元钱。

Input

输入第一行两个正整数N、S,分别表示小Y能预知的天数以及初始时拥有的钱数。接下来N行,第K行三个实数AK、B
K、RateK,意义如题目中所述。对于100%的测试数据,满足:0 0^9。

【提示】

1.输入文件可能很大,请采用快速的读入方式。
2.必然存在一种最优的买卖方案满足:
每次买进操作使用完所有的人民币;
每次卖出操作卖出所有的金券。

Output

只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。

Sample Input

3 100
1 1 1
1 2 2
2 2 3

Sample Output

225.000

题解

这道题hint里面告诉了我们对于我们可以在某一天尽可能多的换成人民币和尽可能多的买入AB劵
然后这道题的斜率方程就可以出来,对于某一天换成最多人民币的价值

表示AB券的价值
表示第i天手头的钱可以换成多少A券,表示第i天手头的钱可以换成多少B券
然后我们发现斜率并不满足单调性。横坐标的增加也不满足单调性。
然后当然可以平衡树维护啦……但是我们有更加好写的cdq分治
cdq分治的思想就是我们把这一段需要处理的区间从中点分成左右两份
一份是一份是
我们先处理左区间,左区间的每个点肯定都是能更新右区间的,我们将左区间的点维护成一个上凸壳,因为是求最大值
将右区间的直线的斜率排序,从大到小排(当然这个排序可以一开始就处理好),从前往后更新,就像斜率和凸壳都满足单调性的时候那样更新
这样我们平均每个节点会被它之前的节点更新次这样复杂度就是

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <cstdio>
  5. #include <vector>
  6. #include <stack>
  7. #define siji(i,x,y) for(int i=(x);i<=(y);++i)
  8. #define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
  9. #define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
  10. #define MAXN 100005
  11. #define mod 1000000007
  12. //#define ivorysi
  13. using namespace std;
  14. typedef long long ll;
  15. ll read() {
  16. ll res=0,neg=1;
  17. char c=getchar();
  18. while(c<'0' || c>'9') {
  19. if(c=='-') neg=-1;
  20. c=getchar();
  21. }
  22. while(c>='0' && c<='9') {
  23. res=res*10+c-'0';
  24. c=getchar();
  25. }
  26. return res*neg;
  27. }
  28. int qry[MAXN],n,tmp[MAXN],s;
  29. double a[MAXN],b[MAXN],rate[MAXN];
  30. struct node {
  31. double x,y;
  32. bool operator < (const node &b) {
  33. return x<b.x || (x==b.x && y<b.y);
  34. }
  35. }que[MAXN],p[MAXN];
  36. double dp[MAXN],ans;
  37. bool cmp(int x,int y) {
  38. return a[x]*b[y]<a[y]*b[x];
  39. }
  40. bool slope(node l,node z,node s) {
  41. return (l.x-s.x)*(z.y-s.y)-(l.y-s.y)*(z.x-s.x) >=0.0;
  42. }
  43. double calc(node l,int z) {
  44. return l.x*a[z]+l.y*b[z];
  45. }
  46. void Cdqsolve(int l,int r) {
  47. if(l==r) {
  48. dp[l]=max(dp[l],dp[l-1]);
  49. ans=max(dp[l],ans);
  50. p[l].y=dp[l]/(a[l]*rate[l]+b[l]);
  51. p[l].x=p[l].y*rate[l];
  52. return;
  53. }
  54. int mid=(l+r)>>1;
  55. int idx1=l,idx2=mid+1,head=1,tail=0;
  56. siji(i,l,r) {
  57. if(qry[i]<=mid) tmp[idx1++]=qry[i];
  58. else tmp[idx2++]=qry[i];
  59. }
  60. siji(i,l,r) qry[i]=tmp[i];
  61. Cdqsolve(l,mid);
  62. siji(i,l,mid) {
  63. while(head<tail && slope(que[tail-1],que[tail],p[i])) --tail;
  64. que[++tail]=p[i];
  65. }
  66. siji(i,mid+1,r) {
  67. while(head<tail && calc(que[head],qry[i])<calc(que[head+1],qry[i])) ++head;
  68. dp[qry[i]]=max(dp[qry[i]],calc(que[head],qry[i]));
  69. }
  70. Cdqsolve(mid+1,r);
  71. if(l==1 && r==n) return;
  72. idx1=l,idx2=mid+1;
  73. head=l;
  74. while(head<=r) {
  75. if(idx2>r || (idx1<=mid && p[idx1]<p[idx2])) que[head++]=p[idx1++];
  76. else que[head++]=p[idx2++];
  77. }
  78. siji(i,l,r) {
  79. p[i]=que[i];
  80. }
  81. return;
  82. }
  83. void trans() {
  84. n=read();s=read();
  85. dp[0]=s;
  86. siji(i,1,n) {
  87. scanf("%lf%lf%lf",&a[i],&b[i],&rate[i]);
  88. qry[i]=i;
  89. }
  90. sort(qry+1,qry+n+1,cmp);
  91. Cdqsolve(1,n);
  92. printf("%.3lf\n",ans);
  93. }
  94. int main() {
  95. #ifdef ivorysi
  96. freopen("f1.in","r",stdin);
  97. #endif
  98. trans();
  99. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注