1. 程式人生 > >BZOJ2800 [Poi2012]Leveling Ground 【擴展歐幾裏得 + 三分 + 堆】

BZOJ2800 [Poi2012]Leveling Ground 【擴展歐幾裏得 + 三分 + 堆】

單個 turn .com HP algorithm 改變 一個 display 否則

題目鏈接

BZOJ2800

題解

區間加極難操作,差分之後可轉化為兩點一加一減
那麽現在問題就將每個點暫時獨立開來

先判定每個點是否被\((A,B)\)整除,否則無解
之後我們先將\(A,B\)化為互質,所有數除一個\((A,B)\)
求得
\[Ax + By = 1\]
那麽對於點\(d[i]\),滿足
\[d[i] = A(xd[i] + kB) + B(yd[i] - kA)\]
其中\(k\)可以取任意值
我們對於單點的目標,是最小化
\[|xd[i] + kB|+|yd[i] - kA|\]
兩個絕對值相加是一個單峰函數,利用三分法即可得出\(k\)
從而得到每個點目前最優解\(X[i] = xd[i] + kB,Y[i] = yd[i] - kA\)

但是我們做到了單個點最優,但整體不一定合法,我們必須滿足正負操作次數相同

\[A\sum\limits_{i = 1}^{n}X[i] + B\sum\limits_{i = 1}^{n}Y[i] = 0\]
而由於\(\sum d[i] = 0\)
故我們只需保證\(T = \sum X[i] = 0\)
顯然我們只需改變\(\frac{T}{B}\)
對於每個\(X[i]\)我們計算出它改變一次的代價,用一個堆維護即可

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector> #include<queue> #include<cmath> #include<map> #define REP(i,n) for (register int i = 1; i <= (n); i++) #define cls(s) memset(s,0,sizeof(s)) #define LL long long int using namespace std; const int maxn = 100005,maxm = 100005,INF = 1000000000; inline int read(){ int out = 0
,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } struct pr{LL v,i;}; inline bool operator <(const pr& a,const pr& b){ return a.v > b.v; } priority_queue<pr> q; LL n,A,B,X,Y,d[maxn],h[maxn],xx[maxn],yy[maxn],dd; void exgcd(LL a,LL b,LL& d,LL& x,LL& y){ if (!b){d = a; x = 1; y = 0;} else exgcd(b,a % b,d,y,x),y -= (a / b) * x; } inline LL cost(int i,LL k){ return abs(X * d[i] + k * B) + abs(Y * d[i] - k * A); } void workmin(){ REP(i,n){ LL l = -INF,r = INF,lmid,rmid,L,R,K; while (r - l >= 3){ lmid = (l + l + r) / 3; rmid = (r + l + r) / 3; L = cost(i,lmid); R = cost(i,rmid); if (L == R){ if (cost(i,lmid - 1) < L) r = rmid; else l = lmid; } else if (L > R) l = lmid; else r = rmid; } K = l; for (int j = l + 1; j <= r; j++) if (cost(i,j) < cost(i,K)) K = j; xx[i] = X * d[i] + K * B; yy[i] = Y * d[i] - K * A; } } inline LL price(int i){ return abs(yy[i] - dd * A) + abs(xx[i] + dd * B) - abs(xx[i]) - abs(yy[i]); } void print(){ //REP(i,n) printf("(%lld,%lld)\n",xx[i],yy[i]); puts(""); LL ans = 0; REP(i,n) ans += abs(xx[i]) + abs(yy[i]); printf("%lld\n",ans >> 1); } void workok(){ LL sum = 0; REP(i,n) sum += xx[i]; sum /= B; dd = sum > 0 ? -1 : 1; sum = abs(sum); REP(i,n) q.push((pr){price(i),i}); pr u; while (sum--){ u = q.top(); q.pop(); xx[u.i] += dd * B; yy[u.i] -= dd * A; q.push((pr){price(u.i),u.i}); } } int main(){ n = read(); A = read(); B = read(); LL D; exgcd(A,B,D,X,Y); A /= D; B /= D; REP(i,n){ h[i] = read(); if (h[i] % D){puts("-1"); return 0;} h[i] /= D; d[i] = h[i] - h[i - 1]; } n++; d[n] = -h[n - 1]; workmin(); workok(); print(); return 0; }

BZOJ2800 [Poi2012]Leveling Ground 【擴展歐幾裏得 + 三分 + 堆】