记录编号 142557 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [国家集训队2011]拆迁队 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 1.167 s
提交时间 2014-12-09 19:23:31 内存使用 37.32 MiB
显示代码纯文本
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<vector>
  6. using namespace std;
  7. typedef long long LL;
  8. const LL INF=(LL)1e18;
  9. const int SIZEN=100010;
  10. class Point{
  11. public:
  12. LL x,y;
  13. };
  14. Point operator - (Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}
  15. LL Cross(Point a,Point b){//叉积,a逆时针旋转到b的有向三角形面积
  16. return a.x*b.y-b.x*a.y;
  17. }
  18. LL Calc(Point p,LL k){//p.x*k+p.y
  19. return p.x*k+p.y;
  20. }
  21. class Hull{//凸包中的点横坐标递增
  22. public:
  23. Point *s;
  24. int n;//0~n-1
  25. //下凸壳,形状类似y=1/x在第一象限的部分
  26. void clear(Point *s_){
  27. s=s_;
  28. n=0;
  29. }
  30. void Insert(Point p){//向凸包后端插入一个点
  31. while(n>=2&&Cross(s[n-1]-p,s[n-2]-p)>=0) n--;
  32. s[n++]=p;
  33. }
  34. Point Find_Best(LL k){//x*k+y最小的点
  35. int l=0,r=n-1;
  36. while(l<r){
  37. int mid=(l+r)/2;
  38. if(Calc(s[mid],k)<=Calc(s[mid+1],k)) r=mid;
  39. else l=mid+1;
  40. }
  41. return s[l];
  42. }
  43. LL Calc_Best(LL k){//凸包上所有点中,x*k+y的最小值
  44. if(!n) return INF;
  45. return Calc(Find_Best(k),k);
  46. }
  47. };
  48. class Node{//线段树的节点
  49. public:
  50. int a,b;//范围
  51. int lc,rc;
  52. Hull H;
  53. };
  54. class Segment_Tree{
  55. public:
  56. Node Tree[SIZEN*2];
  57. int Tree_tot;
  58. Point Hull_Pool[SIZEN*17];
  59. int Hull_tot;
  60. void clear(void){Tree_tot=Hull_tot=0;}
  61. Segment_Tree(void){clear();}//两个指针都初始化为零
  62. Point* Quest_Hull(int n){//申请一块长度为n的凸包池
  63. Point *p=&Hull_Pool[Hull_tot];
  64. Hull_tot+=n;
  65. return p;
  66. }
  67. int Build(int l,int r){
  68. if(l>r) return 0;
  69. int p=++Tree_tot;
  70. Node &now=Tree[p];
  71. now.a=l,now.b=r;
  72. now.H.clear(Quest_Hull(r-l+1));
  73. if(l==r) now.lc=now.rc=0;
  74. else{
  75. int mid=(l+r)/2;
  76. now.lc=Build(l,mid);
  77. now.rc=Build(mid+1,r);
  78. }
  79. return p;
  80. }
  81. void Modify(int root,int x,Point p){//向位置x加入点p
  82. if(!root) return;
  83. Node &now=Tree[root];
  84. if(now.a>x||now.b<x) return;
  85. now.H.Insert(p);
  86. Modify(now.lc,x,p);
  87. Modify(now.rc,x,p);
  88. }
  89. LL Calc_Best(int root,int l,int r,LL k){//位置l~r的点中,x*p+y的最小值
  90. if(!root) return INF;
  91. Node &now=Tree[root];
  92. if(now.a>r||now.b<l) return INF;
  93. if(now.a>=l&&now.b<=r) return now.H.Calc_Best(k);//[l,r]覆盖此段
  94. else return min(Calc_Best(now.lc,l,r,k),Calc_Best(now.rc,l,r,k));
  95. }
  96. };
  97. int N;
  98. LL A[SIZEN],B[SIZEN];
  99. LL F[SIZEN],G[SIZEN];
  100. vector<int> G_pos[SIZEN];//G_pos[i]中是所有G值为i的位置
  101. int G_root[SIZEN]={0};
  102. Segment_Tree SGT;
  103. LL Calc_Const(LL k){//F[k]中和k相关的数
  104. return (k*k-k)/2+A[k]+B[k];
  105. }
  106. Point Turn_Point(LL k){//将k位置的信息转化成点的坐标
  107. Point p;
  108. p.x=A[k]-k;
  109. p.y=F[k]-A[k]*k-A[k]+(k*k+k)/2;
  110. return p;
  111. }
  112. LL Calc(int i,int j){//上一个不拆的是i,这次不拆j
  113. return Calc(Turn_Point(i),j)+Calc_Const(j);
  114. }
  115. void Calc_F(int i){//计算F[i]
  116. if(G[i]<=1){
  117. if(A[i]<i) F[i]=INF;//必须要从1开始吧......
  118. else F[i]=Calc(0,i);//由于A[0]=B[0]=0,所以这么计算是正确的
  119. }
  120. else{
  121. vector<int> &g=G_pos[G[i]-1];
  122. int b=upper_bound(g.begin(),g.end(),i)-g.begin()-1;//找到其离散化后的位置
  123. F[i]=SGT.Calc_Best(G_root[G[i]-1],0,b,i)+Calc_Const(i);
  124. }
  125. }
  126. void Add_To_Tree(int i){//信息加入线段树中
  127. if(F[i]==INF) return;
  128. vector<int> &g=G_pos[G[i]];
  129. int x=lower_bound(g.begin(),g.end(),i)-g.begin();
  130. SGT.Modify(G_root[G[i]],x,Turn_Point(i));
  131. }
  132. bool Cmp_DP_Order(int i,int j){//A[i]-i小的先DP,如果相同那么G小的先DP
  133. if(A[i]-i==A[j]-j) return G[i]<G[j];
  134. return A[i]-i<A[j]-j;
  135. }
  136. void Process(void){//主处理过程
  137. static int lis[SIZEN];
  138. for(int i=1;i<=N;i++) lis[i]=i;
  139. sort(lis+1,lis+1+N,Cmp_DP_Order);
  140. A[0]=A[N+1]=B[0]=B[N+1]=0;//将用到这一性质
  141. for(int i=1;i<=N;i++){
  142. Calc_F(lis[i]);
  143. Add_To_Tree(lis[i]);
  144. }
  145. LL mx=0,ans=INF;
  146. for(int i=1;i<=N;i++) mx=max(mx,G[i]);
  147. for(int i=1;i<=N;i++){
  148. if(G[i]==mx)
  149. ans=min(ans,Calc(i,N+1));//由于A[N+1]=B[N+1]=0所以这么算是对的
  150. }
  151. printf("%lld %lld\n",mx,ans);
  152. }
  153. void Calc_LNDS(LL a[],int n,LL f[]){//求数列a[i]的最长非降子序列
  154. static LL bst[SIZEN];
  155. int len=0;
  156. bst[0]=-INF;
  157. for(int i=1;i<=n;i++){
  158. if(a[i]<0){
  159. f[i]=0;//设为-INF会在按G统计时造成不必要的麻烦
  160. continue;
  161. }
  162. int k=upper_bound(bst,bst+len+1,a[i])-bst;
  163. if(k>len) len=k;
  164. f[i]=k;
  165. bst[k]=a[i];
  166. }
  167. }
  168. void Prepare(void){//预处理
  169. static LL A1[SIZEN];
  170. for(int i=1;i<=N;i++) A1[i]=A[i]-i;
  171. Calc_LNDS(A1,N,G);
  172. for(int i=1;i<=N;i++) G_pos[G[i]].push_back(i);
  173. for(int i=1;i<=N;i++) sort(G_pos[i].begin(),G_pos[i].end());//离散化
  174. for(int i=1;i<=N;i++) G_root[i]=SGT.Build(0,G_pos[i].size()-1);
  175. }
  176. void Read(void){
  177. scanf("%d",&N);
  178. for(int i=1;i<=N;i++) scanf("%lld",&A[i]);
  179. for(int i=1;i<=N;i++) scanf("%lld",&B[i]);
  180. }
  181. int main(){
  182. freopen("lanxisi.in","r",stdin);
  183. freopen("lanxisi.out","w",stdout);
  184. Read();
  185. Prepare();
  186. Process();
  187. return 0;
  188. }