• 【Cf #299 C】Tavas and Pashmaks(单调栈,凸性)


    一个经典的二维数点模型,如果某个人 $ x $ 两个速度都比另一个人 $ y $ 大,显然 $y$ 是不可能成为winner的。

    但这里只考虑两个人$x$,$y$在两个属性各有千秋的时候,一定存在正整数$S$,$R$使得$x$,$y$都有可能成为winner。

    这时考虑单调栈中顶端的两个人$a$和$b$,以及此时准备加入的人$c$,很明显当两两无法比较时,可能存在如下情况:

    1. 当$S$与$R$的比值小于一定值时,$a$总能胜$b$。
    2. 当$S$与$R$的比值大于一定值时,$c$总能胜$b$。

    可以看到,当前者的阀值大于后者的阀值时,就不存在任何一组$S$和$R$,能使$b$成为winner了。(注:这里已经将人按照$s$值排了序)

    前者的不等式大概长这样(当$a$能胜$b$):

    $ frac{ s_{a} \, s_{b} \, ( r_{a} - r_{b} ) }{ r_{a} \, r_{b} \, ( s_{b} - s_{a} ) } > frac{ S }{ R } $

    后者的不等式大概长这样(当$c$能胜$b$):

    $ frac{ s_{c} \, s_{b} \, ( r_{b} - r_{c} ) }{ r_{c} \, r_{b} \, ( s_{c} - s_{b} ) } < frac{ S }{ R } $

    将式子化简后就能判断$b$有没有win的可能性了。

    这里简单证明为什么单调栈做可以不遗漏地剔除所有不合法的人:

    1. 在单调栈维护的过程中,保证了占中连续三个人之间是合法的。
    2. 也就是对于任意的 $x_{i }$,$x_{i+1}$,$x_{i+2}$,假设$x_{i}$能胜$x_{i+1}$的阀值是$v_{i}$,都有$v_{i} < v_{i+1}$,即合法的。
    3. 现在要证明对于在栈中的任意的$x_{i}$,$x_{j}$,$x_{k}$,其中$i < j < k$,都不能使$x_{j}$被剔除。

    考虑另一种思路,可以将以上棘手的证明转化:

    将每一个人的属性以$(frac{1}{s_{i}}, frac{1}{r_{i}})$的形式投射到坐标系上。答案$t = frac{S}{s_{i}} + frac{R}{r_{i}} = (S, R) cdot (frac{1}{s_{i}} + frac{1}{r_{i}})$。

    即在$(S,R)$方向上的投影。

    对于任意一个$(S,R)$,在它上投影相等的点在一条垂直于它的直线上,该直线越靠近原点,投影长度越小。

    枚举的任意一个$(S,R)$就相当于枚举了一条这直线,最先出现在直线平行线上的点就是winner。

    显然,这个点在下凸壳上。并且可以得到,对于下凸壳上的每一个点都存在一组$(S,R)$使得其为winner。

    而我们单调栈所做的就相当于自右下而左上地求出这样一个下凸壳。事实证明,两者的表达式是完全相同的。

    $ igodot $ 技巧&套路:

    • 单调栈的应用,单调栈剔除的变式。
     1 #include <cstdio>
     2 #include <set>
     3 #include <algorithm>
     4 
     5 typedef long long LL;
     6 const int N = 200005;
     7 
     8 struct No {
     9     int x, y, id;
    10     inline friend bool operator < (No a, No b) {
    11         return (a.x == b.x)? (a.y < b.y) : (a.x < b.x);
    12     }
    13 } p[N];
    14 
    15 int n, sta[N], top;
    16 std::set<No> S;
    17 
    18 inline int Check(No a, No b, No c) {
    19     return (LL) a.x * (a.y - b.y) * c.y * (c.x - b.x) > (LL) c.x * (b.y - c.y) * a.y * (b.x - a.x);
    20 }
    21 
    22 int main() {
    23     scanf("%d", &n);
    24     for (int i = 1; i <= n; ++i) {
    25         scanf("%d%d", &p[i].x, &p[i].y);
    26         p[i].id = i;
    27     }
    28     std::sort(p + 1, p + 1 + n);
    29     
    30     for (int i = 1; i <= n; ++i) {
    31         while (top >= 1 && p[sta[top]].y <= p[i].y) --top;
    32         while (top >= 2 && Check(p[sta[top - 1]], p[sta[top]], p[i])) --top;
    33         sta[++top] = i;
    34     }
    35     for (int i = 1; i <= top; ++i) {
    36         S.insert(p[sta[i]]);
    37     }
    38     top = 0;
    39     for (int i = 1; i <= n; ++i) {
    40         if (S.count(p[i])) sta[++top] = p[i].id;
    41     }
    42     std::sort(sta + 1, sta + 1 + top);
    43     for (int i = 1; i <= top; ++i) {
    44         printf("%d ", sta[i]);
    45     }
    46     
    47     return 0;
    48 }
    View Code
  • 相关阅读:
    JVM系列(三)垃圾回收
    JVM系列(二)各区域的OOM
    JVM系列(一)内存模型
    获取IP、mac等信息
    memcached(七)--常用指令抓包分析
    memcached(五)--源码分析,启动
    apt安装mysql + 简易配置
    memcached(六)--spyMemcached的get操作
    解决mysql获取不到连接的问题
    tcp一些要点
  • 原文地址:https://www.cnblogs.com/Dance-Of-Faith/p/9297721.html
Copyright © 2020-2023  润新知