Solution -「多校聯訓」最大面積
阿新 • • 發佈:2021-06-25
\(\mathcal{Description}\)
Link.
平面上有 \(n\) 個點 \(A_{1..n}\),\(q\) 次詢問,每次給出點 \(P\),求
\[\max_{1\le l\le r\le n}\left\{\sum_{i=l}^r \vec{OP}\times\vec{OA_i}\right\}. \]\(n\le10^5\),\(q\le10^6\)。
\(\mathcal{Solution}\)
初步轉化一下式子:
\[\begin{aligned} \sum_{i=l}^r\vec{OP}\times\vec{OA_i}&=\sum_{i=l}^r(x_Py_{A_i}-y_Px_{A_i})\\ &=x_P\left(\sum_{i=l}^ry_{A_i}-\frac{y_P}{x_P}\sum_{i=l}^rx_{A_i}\right)~~~~(x_P\not=0) \end{aligned} \]對於 \(x_P=0\)
並要求最大化 \(x_Pb\)。相當於過某個 \((x(l,r),y(l,r))\) 作斜率為 \(k\) 的直線,求其最大或最小縱截距。若能求出所有 \((x(l,r),y(l,r))\) 構成的凸包,就能在凸殼上二分求解了。
考慮求凸包的方法:分治,對於分支區間 \([l,r]\)
複雜度 \(\mathcal O(n\log^2n+q\log n)\)。
\(\mathcal{Code}\)
/* Rainybunny */ #include <cstdio> #include <vector> #include <algorithm> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i ) #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) typedef long long LL; typedef long double LD; inline int rint() { int x = 0, f = 1, s = getchar(); for ( ; s < '0' || '9' < s; s = getchar() ) f = s == '-' ? -f : f; for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' ); return x * f; } template<typename Tp> inline void wint( Tp x ) { if ( x < 0 ) putchar( '-' ), x = -x; if ( 9 < x ) wint( x / 10 ); putchar( x % 10 ^ '0' ); } inline LL lmax( const LL a, const LL b ) { return a < b ? b : a; } namespace PGP { const LD EPS = 1e-9; inline int dcmp( const LD a ) { return -EPS < a && a < EPS ? 0 : a < 0 ? -1 : 1; } struct Point { LL x, y; Point(): x( 0 ), y( 0 ) {} Point( const LL a, const LL b ): x( a ), y( b ) {} inline Point operator + ( const Point& p ) const { return { x + p.x, y + p.y }; } inline Point operator - ( const Point& p ) const { return { x - p.x, y - p.y }; } inline int operator ^ ( const Point& p ) const { return dcmp( LD( x ) * p.y - LD( y ) * p.x ); } inline bool operator < ( const Point& p ) const { return x != p.x ? x < p.x : y < p.y; } inline bool operator == ( const Point& p ) const { return x == p.x && y == p.y; } }; typedef Point Vector; typedef std::vector<Point> Convex; inline Convex getConvex( std::vector<Point> vec ) { static Convex ret; ret.clear(); std::sort( vec.begin(), vec.end() ); vec.resize( std::unique( vec.begin(), vec.end() ) - vec.begin() ); int n = int( vec.size() ), top = 0; ret.resize( n << 1 ); rep ( i, 0, n - 1 ) { for ( ; top > 1; --top ) { if ( ( ( ret[top - 1] - ret[top - 2] ) ^ ( vec[i] - ret[top - 2] ) ) > 0 ) break; } ret[top++] = vec[i]; } for ( int tmp = top, i = n - 2; ~i; --i ) { for ( ; top > tmp; --top ) { if ( ( ( ret[top - 1] - ret[top - 2] ) ^ ( vec[i] - ret[top - 2] ) ) > 0 ) break; } ret[top++] = vec[i]; } top -= n > 1; return ret.resize( top ), ret; } inline int findPole( const Convex& cvx ) { int ret = -1, n = int( cvx.size() ); rep ( i, 0, n - 1 ) { if ( !~ret || cvx[ret].y > cvx[i].y || ( cvx[ret].y == cvx[i].y && cvx[ret].x > cvx[i].x ) ) { ret = i; } } return ret; } inline void poleSort( Convex& cvx ) { int pid = findPole( cvx ), n = int( cvx.size() ); pid = n - pid - 1; std::reverse( cvx.begin(), cvx.end() ); std::reverse( cvx.begin(), cvx.begin() + pid + 1 ); std::reverse( cvx.begin() + pid + 1, cvx.end() ); } inline Convex minkowskiSum( Convex A, Convex B ) { static Convex ret; ret.clear(); poleSort( A ), poleSort( B ); int n = int( A.size() ), m = int( B.size() ); Point ap( A[0] ), bp( B[0] ); ret.push_back( ap + bp ); rep ( i, 0, n - 2 ) A[i] = A[i + 1] - A[i]; A[n - 1] = ap - A[n - 1]; rep ( i, 0, m - 2 ) B[i] = B[i + 1] - B[i]; B[m - 1] = bp - B[m - 1]; int i = 0, j = 0; while ( i < n && j < m ) { // 注意這裡能 hack,應該計算極角來比較,否則無法正確處理共線向量。 ret.push_back( ret.back() + ( ( A[i] ^ B[j] ) > 0 ? A[i++] : B[j++] ) ); } while ( i < n ) ret.push_back( ret.back() + A[i++] ); while ( j < m ) ret.push_back( ret.back() + B[j++] ); return ret; } } using namespace PGP; const int MAXN = 1e5; int n, q, rpos; Point A[MAXN + 5]; Convex cvxA; LL mnsec, mxsec; inline void buildConvex( const int l, const int r ) { if ( l == r ) return cvxA.push_back( A[l] ); int mid = l + r >> 1; buildConvex( l, mid ), buildConvex( mid + 1, r ); static Convex cvxL, cvxR; cvxL.clear(), cvxR.clear(); Point cur; per ( i, mid, l ) cvxL.push_back( cur = cur + A[i] ); cur = Point(); rep ( i, mid + 1, r ) cvxR.push_back( cur = cur + A[i] ); cvxL = minkowskiSum( getConvex( cvxL ), getConvex( cvxR ) ); for ( const auto& p: cvxL ) cvxA.push_back( p ); } inline void init() { buildConvex( 1, n ), cvxA = getConvex( cvxA ); #ifdef RYBY puts( "+++ cvxA +++" ); for ( auto p: cvxA ) printf( "%lld %lld\n", p.x, p.y ); puts( "--- cvxA ---" ); #endif if ( cvxA.size() == 1 ) rpos = 0; else { rpos = -1; rep ( i, 1, cvxA.size() - 1 ) { if ( cvxA[i].x <= cvxA[i - 1].x ) { rpos = i - 1; break; } } if ( !~rpos ) rpos = cvxA.size() - 1; } LL las = 0; rep ( i, 1, n ) { if ( ( las += A[i].x ) > mxsec ) mxsec = las; if ( las < 0 ) las = 0; } las = 0; rep ( i, 1, n ) { if ( ( las += A[i].x ) < mnsec ) mnsec = las; if ( las > 0 ) las = 0; } } inline LL solve( const Point& P ) { if ( !P.x ) return P.y > 0 ? -P.y * mnsec : -P.y * mxsec; if ( cvxA.size() == 1 ) return P ^ cvxA[0]; Vector kp( P.x, P.y ); if ( kp.x < 0 ) kp.x = -kp.x, kp.y = -kp.y; int s = int( cvxA.size() ); if ( P.x > 0 ) { // up convex. int l = rpos, r = s; while ( l < r ) { int mid = l + r >> 1; if ( ( ( cvxA[mid] - cvxA[( mid + 1 ) % s] ) ^ kp ) > 0 ) l = mid + 1; else r = mid; } l %= s; return P.x * cvxA[l].y - P.y * cvxA[l].x; } else { // down convex. int l = 0, r = rpos; while ( l < r ) { int mid = l + r >> 1; if ( ( ( cvxA[( mid + 1 ) % s] - cvxA[mid] ) ^ kp ) > 0 ) l = mid + 1; else r = mid; } l %= s; return P.x * cvxA[l].y - P.y * cvxA[l].x; } } int main() { freopen( "area.in", "r", stdin ); freopen( "area.out", "w", stdout ); n = rint(), q = rint(); rep ( i, 1, n ) A[i].x = rint(), A[i].y = rint(); init(); for ( Point P; q--; ) { P.x = rint(), P.y = rint(); wint( lmax( solve( P ), 0 ) ), putchar( '\n' ); } return 0; }