1 条题解
-
0
题解
把方向向量记作 ,当两条平行线的法向为 时,所有点在 \\vec d^\\perp 上的投影构成一条数轴,求“某一段连续区间的权值和最大”。也就是说,只要知道当前方向下点的投影顺序,就能通过最大子段和得到答案;当方向旋转时,投影顺序只会在相邻的两点投影重合时发生交换。
因此采用“旋转扫描 + 线段树维护最大子段和”的常规套路:
- 初始方向设为 轴正向,按 排序作为初始投影顺序,在线段树上建权值数组。
- 对每一对点 ,当方向与向量 垂直时,二者投影相等,此时顺序会交换。向量的垂直向量记作 ,把它化成有向单位(同向归一)后作为事件的键。
- 将所有事件按键排序,角度相同的一组一起处理;在该角度附近,只有这些事件对应的点对可能交换。旋转的连续性保证它们在处理时已经相邻,因此只要检查是否相邻即可执行交换,同时在线段树做两次单点修改。组处理完更新一次答案。
线段树节点维护区间和、前缀最大、后缀最大、子段最大,交换两个相邻位置仅需两次单点更新。事件数为 ,整体复杂度 ,内存 ,可满足 。
#include <bits/stdc++.h> using namespace std; struct Node { long long sum, pref, suff, best; }; struct SegTree { int n; vector<Node> st; SegTree(const vector<long long>& a){ n=a.size(); st.resize(4*n); build(1,0,n-1,a); } Node merge(const Node& L,const Node& R){ return {L.sum+R.sum, max(L.pref, L.sum+R.pref), max(R.suff, R.sum+L.suff), max({L.best,R.best,L.suff+R.pref})}; } void build(int p,int l,int r,const vector<long long>& a){ if(l==r){ long long v=a[l]; st[p]={v,v,v,v}; return; } int m=(l+r)>>1; build(p<<1,l,m,a); build(p<<1|1,m+1,r,a); st[p]=merge(st[p<<1],st[p<<1|1]); } void update(int p,int l,int r,int idx,long long v){ if(l==r){ st[p]={v,v,v,v}; return; } int m=(l+r)>>1; if(idx<=m) update(p<<1,l,m,idx,v); else update(p<<1|1,m+1,r,idx,v); st[p]=merge(st[p<<1],st[p<<1|1]); } void update(int idx,long long v){ update(1,0,n-1,idx,v); } long long best() const { return st[1].best; } }; struct Point{ long long x,y,w; }; struct Event{ long long vx,vy; int a,b; }; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int N; if(!(cin>>N)) return 0; vector<Point> p(N); for(int i=0;i<N;++i) cin>>p[i].x>>p[i].y>>p[i].w; vector<int> order(N); iota(order.begin(),order.end(),0); sort(order.begin(),order.end(),[&](int a,int b){ return p[a].x==p[b].x ? p[a].y<p[b].y : p[a].x<p[b].x; }); vector<int> pos(N); vector<long long> cur(N); for(int i=0;i<N;++i){ pos[order[i]]=i; cur[i]=p[order[i]].w; } SegTree seg(cur); long long ans=seg.best(); vector<Event> ev; ev.reserve(1LL*N*(N-1)/2); for(int i=0;i<N;++i) for(int j=i+1;j<N;++j){ long long dx=p[j].x-p[i].x, dy=p[j].y-p[i].y; long long vx=-dy, vy=dx; long long g=std::gcd(std::llabs(vx), std::llabs(vy)); if(g){vx/=g; vy/=g;} if(vy<0 || (vy==0 && vx<0)){vx=-vx; vy=-vy;} ev.push_back({vx,vy,i,j}); } auto half=[](long long x,long long y){ return y>0 || (y==0 && x>0); }; auto cmp=[&](const Event& A,const Event& B){ bool ha=half(A.vx,A.vy), hb=half(B.vx,B.vy); if(ha!=hb) return ha<hb; return A.vy*B.vx < A.vx*B.vy; }; sort(ev.begin(),ev.end(),cmp); size_t i=0; while(i<ev.size()){ size_t j=i; while(j<ev.size() && ev[j].vx==ev[i].vx && ev[j].vy==ev[i].vy) ++j; for(size_t k=i;k<j;++k){ int a=ev[k].a, b=ev[k].b; int pa=pos[a], pb=pos[b]; if(pa>pb){ swap(pa,pb); swap(a,b); } if(pb-pa==1){ swap(order[pa],order[pb]); swap(cur[pa],cur[pb]); pos[a]=pb; pos[b]=pa; seg.update(pa,cur[pa]); seg.update(pb,cur[pb]); } } ans=max(ans, seg.best()); i=j; } cout<<ans<<'\n'; return 0; }
- 1
信息
- ID
- 5716
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者