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)的了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #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\n" ,ans); return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
· SQL Server统计信息更新会被阻塞或引起会话阻塞吗?
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 本地部署 DeepSeek:小白也能轻松搞定!
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)