• UR #13 Ernd


    考试的时候没有注意到可以将(a,b)放在二维平面上之后旋转坐标系,使得转移变成树状数组二维偏序

    这样就算我想出来了第二个转移的斜率优化也没有什么卵用啊(摔西瓜

    设g(i)表示当前站在第i个水果下面且第i个水果此时并没有记分的最大得分

    设f(i)表示当前站在第i个水果下面且第i个水果此时已经记分的最大得分

    g(i)=max(f(j)) (满足j能到达i)

    f(i)=max(g(j)+(i-j+1)^2)(满足从i出发可以一路接水果走到j)

    首先考虑g(i)的转移,我们注意到j能到达i当且仅当

    abs(ai-aj)<=bi-bj

    又注意到bi>=bj

    我们对绝对值进行分类讨论后可以得到j可以到达的i都在j的10点半钟方向到13点半钟方向

    之后我们将坐标系旋转,即将每个点的坐标变为(bi+ai,bi-ai)

    那么j能到达的i都在j的右上方啦,那么对于这个转移就变成了一个二维偏序问题

    对于一维排序,另一维用树状数组维护即可,时间复杂度O(nlogn)

    之后我们考虑f(i)的转移,我们很容易发现这是可以分段的

    对于每个i不能到达i+1分一段,那么这个转移每一段都是相互独立的

    我们又惊奇的发现在第一个转移排序后,每一段内的相对顺序是不变的

    注意到这是一个很经典的可以用斜率优化的式子

    化简完后f(i)=max(g(j)-2*j+j*j-2*i*j)+(i+1)^2

    对于斜率2*i,我们用很容易发现这个斜率是递增的

    我们可以对于每一段用单调栈维护一个上凸包,这样这个转移就是O(n)的了

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    typedef long long LL;
    const int maxn=500010;
    int n,cnt;
    int a[maxn],b[maxn];
    int pos[maxn];
    LL dp[maxn];
    LL t[maxn];
    LL m1,m2,ans;
    struct Point{
    	LL x,y,id;
    	Point(LL x=0,LL y=0,LL id=0):x(x),y(y),id(id){}
    }p[maxn];
    vector<Point>V[maxn];
    int top[maxn];
    typedef Point Vector;
    Vector operator -(const Point &A,const Point &B){return Vector(A.x-B.x,A.y-B.y);}
    LL Cross(const Vector &A,const Vector &B){return A.x*B.y-A.y*B.x;}
    bool cmpy(const Point &A,const Point &B){return A.y<B.y;}
    bool cmpx(const Point &A,const Point &B){
    	if(A.x==B.x&&A.y==B.y)return A.id<B.id;
    	if(A.x==B.x)return A.y<B.y;
    	return A.x<B.x;
    }
    void read(int &num){
    	num=0;char ch=getchar();
    	while(ch<'!')ch=getchar();
    	while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
    }
    int lowbit(int x){return x&(-x);}
    void modify(int x,LL v){for(int i=x;i<=cnt;i+=lowbit(i))t[i]=max(t[i],v);}
    LL ask(int x){
    	LL mx=0;
    	for(int i=x;i>=1;i-=lowbit(i))mx=max(mx,t[i]);
    	return mx;
    }
    LL Get_ans(int id,LL k){
    	while(V[id].size()>1&&V[id][top[id]].y-2*k*V[id][top[id]].x<=V[id][top[id]-1].y-2*k*V[id][top[id]-1].x)top[id]--,V[id].pop_back();
    	if(!V[id].empty())return V[id][top[id]].y-2*k*V[id][top[id]].x+(k+1)*(k+1);
    	else return 0;
    }
    void push(int id,Point P){
    	while(V[id].size()>1&&Cross(P-V[id][top[id]],V[id][top[id]]-V[id][top[id]-1])<=0)top[id]--,V[id].pop_back();
    	top[id]++;V[id].push_back(P);
    }
    
    int main(){
    	read(n);
    	for(int i=1;i<=n;++i){
    		read(a[i]);read(b[i]);
    		p[i]=Point(b[i]+a[i],b[i]-a[i],i);
    	}
    	for(int i=1;i<=n;++i){
    		if(i==1||abs(a[i]-a[i-1])>b[i]-b[i-1])pos[i]=pos[i-1]+1;
    		else pos[i]=pos[i-1];
    	}
    	for(int i=1;i<=n;++i)a[i]=b[i]-a[i];
    	sort(a+1,a+n+1);sort(p+1,p+n+1,cmpy);
    	for(int i=1;i<=n;++i){
    		if(i==1||a[i]!=a[i-1])p[i].y=++cnt;
    		else p[i].y=cnt;
    	}
    	sort(p+1,p+n+1,cmpx);
    	memset(top,-1,sizeof(top));
    	for(int i=1;i<=n;++i){
    		m1=ask(p[i].y);m2=Get_ans(pos[p[i].id],p[i].id);
    		dp[p[i].id]=max(m1+1,m2);
    		ans=max(dp[p[i].id],ans);
    		modify(p[i].y,dp[p[i].id]);
    		push(pos[p[i].id],Point(p[i].id,m1-2*p[i].id+p[i].id*p[i].id));
    	}printf("%lld
    ",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    SQL高效运行注意事项(四)
    SQL Serve里DBA要去改变的3个配置选项
    sql还原数据库时候,遇到数据库被占用的解决情况
    sqlserver中将datetime类型转换为yyyyMMddHHmmss格式
    SQL 高效运行注意事项(三)
    当您解开后您从 Internet 上下载的压缩的文件时,文件的修改日期更改为您提取它的日期
    MySQL通过自定义函数实现递归查询父级ID或者子级ID
    YII2集成GOAOP,实现面向方面编程!
    C语言关于指针的注意事项
    转载 could not find a getter for ... in class ... 异常的原因解析
  • 原文地址:https://www.cnblogs.com/joyouth/p/5381133.html
Copyright © 2020-2023  润新知