1. 程式人生 > >[Bzoj4570][Scoi2016]妖怪(右上凸包)

[Bzoj4570][Scoi2016]妖怪(右上凸包)

愛好者 屬性。 判斷 常數 接下來 斜率 inpu std com

4570: [Scoi2016]妖怪


Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1110 Solved: 336
[Submit][Status][Discuss]

Description


邱老師是妖怪愛好者,他有n只妖怪,每只妖怪有攻擊力atk和防禦力dnf兩種屬性。邱老師立誌成為妖怪大師,於 是他從真新鎮出發,踏上未知的旅途,見識不同的風景。環境對妖怪的戰鬥力有很大影響,在某種環境中,妖怪可 以降低自己k×a點攻擊力,提升k×b點防禦力或者,提升自己k×a點攻擊力,降低k×b點防禦力,a,b屬於正實數 ,k為任意實數,但是atk和dnf必須始終非負。妖怪在環境(a,b)中的戰鬥力為妖怪在該種環境中能達到的最大攻擊 力和最大防禦力之和。strength(a,b)=max(atk(a,b))+max(dnf(a,b))環境由a,b兩個參數定義,a,b的含義見前 文描述。比如當前環境a=3,b=2,那麽攻擊力為6,防禦力為2的妖怪,能達到的最大攻擊力為9,最大防禦力為6。 所以該妖怪在a=3,b=2的環境下戰鬥力為15。因此,在不同的環境,戰鬥力最強的妖怪可能發生變化。作為一名優 秀的妖怪訓練師,邱老師想發掘每一只妖怪的最大潛力,他想知道在最為不利的情況下,他的n只妖怪能夠達到的 最強戰鬥力值,即存在一組正實數(a,b)使得n只妖怪在該環境下最強戰鬥力最低。

Input


第一行一個n,表示有n只妖怪。接下來n行,每行兩個整數atk和dnf,表示妖怪的攻擊力和防禦力。 1≤n≤10^6, 0<atk,dnf≤10^8

Output


輸出在最不利情況下最強妖怪的戰鬥力值,保留4位小數。

Sample Input


3 
1 1 
1 2 
2 2

Sample Output


8.0000

分析


雖然是看了別人的博客才寫起的,但也要寫博客加深印象。 設atk為x,dnf為y。那麽每個妖怪能看成平面上的一個點(x,y) 設最強戰鬥力為L 技術分享圖片
合並一下 技術分享圖片

發現是L是一條斜率-b/a的直線。

如果已知直線L,在它下最強的妖怪是誰呢?

那麽畫圖來試試,一大堆小點點是一個妖怪,然後右上角是維護的右上凸包

技術分享圖片

發現粉色直線與右上凸包的切點是在它下的最強妖怪

為什麽是右上凸包與它切點最優,因為我們答案是橫縱截距之和,我們把直線往下平移發現橫縱截距都在減小,肯定不會比切點的戰鬥力小

已知右上凸包怎麽對右上凸包每個點求出一個最優的斜率k使它為切點?

可以得出結論:斜率一定是大於它與上一個點連線的斜率,小於它與下一個點連線的斜率

設右上凸包每一個點與下一個點連線斜率為ki

那麽斜率k就在這個範圍內技術分享圖片,我們要在這個範圍內求一個點使答案最小。

當我們已知點(x,y)求L時,x,y變成了常數,又斜率k為-b/a

那麽答案可以表示為技術分享圖片

發現是個雙鉤函數,在k = -(x/y)^½時取最小。

因為是雙鉤函數不滿足單調,我就去三分了,然後華麗麗T了。

其實我們可以判斷一下雙鉤函數頂點是否在合法範圍內,是的話取最小值

不是的話肯定是關於雙鉤函數的一邊,則具有單調性。

那麽此時我就想到二分了。。。然後完美地t了

正解:

合法範圍ki-1和ki一定是雙鉤函數定義域的端點。每個點可以用ki算一次答案,如果頂點在定義域內還可以算一次答案

AC代碼:


# include <iostream>
# include <cstdio>
# include <cstring>
# include <cmath>
# include <algorithm>
using namespace std;
const int N = 1e6 + 12;
const double eps = 1e-6;
const double inf = 0x3f3f3f3f;
struct Point{
    double x,y;
    Point(){}
    Point(double a,double b) : x(a),y(b){}
    void read(){scanf("%lf %lf",&x,&y);}
    double com(){return x + y + 2 * sqrt(x * y);}
    bool operator <(const Point & other)const{return x == other.x ? y > other.y : x > other.x;}
}a[N];
int n,ed;double k[N],ans = inf;
Point operator -(Point a,Point b){return Point(a.x - b.x,a.y - b.y);}
double Get(Point a){return a.y / a.x;}
double Cross(Point a,Point b){return a.x * b.y - a.y * b.x;}
int main()
{
  scanf("%d",&n);
  for(int i = 1;i <= n;i++)a[i].read();
  sort(a + 1,a + n + 1);int top = 1;
  for(int i = 2;i <= n;i++)
  {
      while(top > 1 && Cross(a[i] - a[top],a[top] - a[top - 1]) >= 0)top--;
      a[++top] = a[i];
  }
  a[top + 1] = Point(0,0);
  for(int i = 1;i <= top;i++)
  {
      k[i] = Get(a[i + 1] - a[i]);
      ed = i;if(k[i] >= 0)break;
  }
  k[ed] = -eps;k[0] = -inf;
  for(int i = 1;i <= ed;i++)
  {    
      double mk = -sqrt(a[i].y / a[i].x),cg = inf,he;
      if(mk > k[i - 1] && mk < k[i])cg = a[i].com();
      he = a[i].x + a[i].y - a[i].x * k[i] - a[i].y / k[i];
      cg = cg > he ? 
    he : cg;ans = ans > cg ? cg : ans;
  }
  printf("%.4lf\n",ans);
}

[Bzoj4570][Scoi2016]妖怪(右上凸包)