记录编号 |
142557 |
评测结果 |
AAAAAAAAAAAAAAAAAAAA |
题目名称 |
[国家集训队2011]拆迁队 |
最终得分 |
100 |
用户昵称 |
cstdio |
是否通过 |
通过 |
代码语言 |
C++ |
运行时间 |
1.167 s |
提交时间 |
2014-12-09 19:23:31 |
内存使用 |
37.32 MiB |
显示代码纯文本
- #include<iostream>
- #include<cstdio>
- #include<algorithm>
- #include<cstring>
- #include<vector>
- using namespace std;
- typedef long long LL;
- const LL INF=(LL)1e18;
- const int SIZEN=100010;
- class Point{
- public:
- LL x,y;
- };
- Point operator - (Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}
- LL Cross(Point a,Point b){//叉积,a逆时针旋转到b的有向三角形面积
- return a.x*b.y-b.x*a.y;
- }
- LL Calc(Point p,LL k){//p.x*k+p.y
- return p.x*k+p.y;
- }
- class Hull{//凸包中的点横坐标递增
- public:
- Point *s;
- int n;//0~n-1
- //下凸壳,形状类似y=1/x在第一象限的部分
- void clear(Point *s_){
- s=s_;
- n=0;
- }
- void Insert(Point p){//向凸包后端插入一个点
- while(n>=2&&Cross(s[n-1]-p,s[n-2]-p)>=0) n--;
- s[n++]=p;
- }
- Point Find_Best(LL k){//x*k+y最小的点
- int l=0,r=n-1;
- while(l<r){
- int mid=(l+r)/2;
- if(Calc(s[mid],k)<=Calc(s[mid+1],k)) r=mid;
- else l=mid+1;
- }
- return s[l];
- }
- LL Calc_Best(LL k){//凸包上所有点中,x*k+y的最小值
- if(!n) return INF;
- return Calc(Find_Best(k),k);
- }
- };
- class Node{//线段树的节点
- public:
- int a,b;//范围
- int lc,rc;
- Hull H;
- };
- class Segment_Tree{
- public:
- Node Tree[SIZEN*2];
- int Tree_tot;
- Point Hull_Pool[SIZEN*17];
- int Hull_tot;
- void clear(void){Tree_tot=Hull_tot=0;}
- Segment_Tree(void){clear();}//两个指针都初始化为零
- Point* Quest_Hull(int n){//申请一块长度为n的凸包池
- Point *p=&Hull_Pool[Hull_tot];
- Hull_tot+=n;
- return p;
- }
- int Build(int l,int r){
- if(l>r) return 0;
- int p=++Tree_tot;
- Node &now=Tree[p];
- now.a=l,now.b=r;
- now.H.clear(Quest_Hull(r-l+1));
- if(l==r) now.lc=now.rc=0;
- else{
- int mid=(l+r)/2;
- now.lc=Build(l,mid);
- now.rc=Build(mid+1,r);
- }
- return p;
- }
- void Modify(int root,int x,Point p){//向位置x加入点p
- if(!root) return;
- Node &now=Tree[root];
- if(now.a>x||now.b<x) return;
- now.H.Insert(p);
- Modify(now.lc,x,p);
- Modify(now.rc,x,p);
- }
- LL Calc_Best(int root,int l,int r,LL k){//位置l~r的点中,x*p+y的最小值
- if(!root) return INF;
- Node &now=Tree[root];
- if(now.a>r||now.b<l) return INF;
- if(now.a>=l&&now.b<=r) return now.H.Calc_Best(k);//[l,r]覆盖此段
- else return min(Calc_Best(now.lc,l,r,k),Calc_Best(now.rc,l,r,k));
- }
- };
- int N;
- LL A[SIZEN],B[SIZEN];
- LL F[SIZEN],G[SIZEN];
- vector<int> G_pos[SIZEN];//G_pos[i]中是所有G值为i的位置
- int G_root[SIZEN]={0};
- Segment_Tree SGT;
- LL Calc_Const(LL k){//F[k]中和k相关的数
- return (k*k-k)/2+A[k]+B[k];
- }
- Point Turn_Point(LL k){//将k位置的信息转化成点的坐标
- Point p;
- p.x=A[k]-k;
- p.y=F[k]-A[k]*k-A[k]+(k*k+k)/2;
- return p;
- }
- LL Calc(int i,int j){//上一个不拆的是i,这次不拆j
- return Calc(Turn_Point(i),j)+Calc_Const(j);
- }
- void Calc_F(int i){//计算F[i]
- if(G[i]<=1){
- if(A[i]<i) F[i]=INF;//必须要从1开始吧......
- else F[i]=Calc(0,i);//由于A[0]=B[0]=0,所以这么计算是正确的
- }
- else{
- vector<int> &g=G_pos[G[i]-1];
- int b=upper_bound(g.begin(),g.end(),i)-g.begin()-1;//找到其离散化后的位置
- F[i]=SGT.Calc_Best(G_root[G[i]-1],0,b,i)+Calc_Const(i);
- }
- }
- void Add_To_Tree(int i){//信息加入线段树中
- if(F[i]==INF) return;
- vector<int> &g=G_pos[G[i]];
- int x=lower_bound(g.begin(),g.end(),i)-g.begin();
- SGT.Modify(G_root[G[i]],x,Turn_Point(i));
- }
- bool Cmp_DP_Order(int i,int j){//A[i]-i小的先DP,如果相同那么G小的先DP
- if(A[i]-i==A[j]-j) return G[i]<G[j];
- return A[i]-i<A[j]-j;
- }
- void Process(void){//主处理过程
- static int lis[SIZEN];
- for(int i=1;i<=N;i++) lis[i]=i;
- sort(lis+1,lis+1+N,Cmp_DP_Order);
- A[0]=A[N+1]=B[0]=B[N+1]=0;//将用到这一性质
- for(int i=1;i<=N;i++){
- Calc_F(lis[i]);
- Add_To_Tree(lis[i]);
- }
- LL mx=0,ans=INF;
- for(int i=1;i<=N;i++) mx=max(mx,G[i]);
- for(int i=1;i<=N;i++){
- if(G[i]==mx)
- ans=min(ans,Calc(i,N+1));//由于A[N+1]=B[N+1]=0所以这么算是对的
- }
- printf("%lld %lld\n",mx,ans);
- }
- void Calc_LNDS(LL a[],int n,LL f[]){//求数列a[i]的最长非降子序列
- static LL bst[SIZEN];
- int len=0;
- bst[0]=-INF;
- for(int i=1;i<=n;i++){
- if(a[i]<0){
- f[i]=0;//设为-INF会在按G统计时造成不必要的麻烦
- continue;
- }
- int k=upper_bound(bst,bst+len+1,a[i])-bst;
- if(k>len) len=k;
- f[i]=k;
- bst[k]=a[i];
- }
- }
- void Prepare(void){//预处理
- static LL A1[SIZEN];
- for(int i=1;i<=N;i++) A1[i]=A[i]-i;
- Calc_LNDS(A1,N,G);
- for(int i=1;i<=N;i++) G_pos[G[i]].push_back(i);
- for(int i=1;i<=N;i++) sort(G_pos[i].begin(),G_pos[i].end());//离散化
- for(int i=1;i<=N;i++) G_root[i]=SGT.Build(0,G_pos[i].size()-1);
- }
- void Read(void){
- scanf("%d",&N);
- for(int i=1;i<=N;i++) scanf("%lld",&A[i]);
- for(int i=1;i<=N;i++) scanf("%lld",&B[i]);
- }
- int main(){
- freopen("lanxisi.in","r",stdin);
- freopen("lanxisi.out","w",stdout);
- Read();
- Prepare();
- Process();
- return 0;
- }