zoukankan      html  css  js  c++  java
  • CF1045F Shady Lady【结论,计算几何】

    给定 (n) 个形如 (x^ay^b) 的项,Ani 先删去至多一项,Borna 再填正整数系数,当多项式有下界时 Borna 赢,否则 Ani 赢,求谁赢。

    (nle 2cdot 10^5)(0le a,ble 10^9)


    称一堆单项式有下界当且仅当可以填正整数系数,使得该多项式有下界。

    题目实际上是给定一堆单项式,求是否可以删去至多一项使得没有下界。

    引理:一堆单项式有下界 (Leftrightarrow) 将每一项 (x^ay^b ightarrow(a,b)) 画在平面上,带上原点 ((0,0)) 组成的点集的凸包的所有顶点的横纵坐标均为偶数。

    证明:先考虑 (Leftarrow),设 (P_1,P_2,cdots,P_m) 是凸包顶点,则对于任意凸包内部整点 (Q(a,b)),存在有理数 (alpha_1,cdots,alpha_m) 满足 (alpha_ige 0)(sumalpha_i=1)(Q=sumalpha_iP_i),则根据加权算术-几何平均不等式,(sumalpha_ix^{P_i.a}y^{P_i.b}=sumalpha_i(x^2)^{P_i.a/2}(y^2)^{P_i.b/2}geprod((x^2)^{P_i.a/2}(y^2)^{P_i.b/2})^{alpha_i}=(x^2)^{a/2}(y^2)^{b/2}=|x^ay^b|),由此可得令凸包顶点的系数为 (n),内部点的系数为 (1) 时多项式有下界,得证。

    再考虑 (Rightarrow),设凸包顶点 (P_k) 的横纵坐标不全为偶数,不妨设横坐标为奇数。设凸包在 (P_k) 处的法向量为 ((p,q)),则在所有 (P_i)(pa+qb)(P_k) 是唯一最值,若为最小值则将 (p,q) 取反,此时 (pcdot P_k.a+qcdot P_k.b>pcdot 0+qcdot 0=0),所以令 (x=-t^p,y=t^q,t ightarrow+infty) 时多项式的值 ( ightarrow-infty),得证。

    于是问题转化为给定点集问是否可以删去至多一个点,使得凸包上至少有一个点横纵坐标有至少一个是奇数。

    官方题解给了个神奇做法:先求出凸包,然后给凸包顶点(除原点)交替染黑白色,分别对不是黑/白色的点求凸包,如果有一个有横纵坐标有奇数的顶点,那么 Ani 赢(可以),否则 Borna 赢。

    至于证明,因为没给所以不会,感性理解就是若不删点就满足要求则显然正确,否则看得到的奇数顶点,至少且只需要删掉现有凸包顶点的一个区间,此时满足做法中判断的条件就说明只需要删去一个点,也是正确的。

    时间复杂度 (O(nlog n))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N = 200003;
    template<typename T>
    void rd(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    }
    int n, m, tp, h[N], p[N];
    struct Node {
    	int x, y, c;
    	Node(int _x = 0, int _y = 0, int _c = 0): x(_x), y(_y), c(_c){}
    	Node operator-(const Node &o) const {return Node(x-o.x, y-o.y);}
    	bool operator<(const Node &o) const {return x<o.x || x==o.x && y<o.y;}
    	LL operator*(const Node &o) const {return (LL)x*o.y-(LL)y*o.x;}
    	operator bool() const {return (x|y)&1;}
    } a[N];
    void work(int *h){
    	tp = 0;
    	for(int i = 0;i <= n;++ i) if(a[i].c != 1){
    		while(tp > 1 && (a[i]-a[h[tp]])*(a[h[tp]]-a[h[tp-1]])>=0) --tp;
    		h[++tp] = i;
    	} int tmp = tp;
    	for(int i = n-1;~i;-- i) if(a[i].c != 1){
    		while(tp > tmp && (a[i]-a[h[tp]])*(a[h[tp]]-a[h[tp-1]])>=0) --tp;
    		h[++tp] = i;
    	}
    }
    int main(){
    	rd(n);
    	for(int i = 1;i <= n;++ i){
    		rd(a[i].x); rd(a[i].y);
    	} sort(a, a+n+1);
    	work(p); m = tp;
    	for(int i = 2;i < tp;++ i) if(a[p[i]]){puts("Ani"); return 0;}
    	for(int i = 2;i < m;++ i) a[p[i]].c = 2 - (i&1);
    	work(h);
    	for(int i = 2;i < tp;++ i) if(a[h[i]]){puts("Ani"); return 0;}
    	for(int i = 2;i < m;++ i) a[p[i]].c = 1 + (i&1);
    	work(h);
    	for(int i = 2;i < tp;++ i) if(a[h[i]]){puts("Ani"); return 0;}
    	puts("Borna");
    }
    
  • 相关阅读:
    LeetCode155. 最小栈
    LeetCode154. 寻找旋转排序数组中的最小值 II
    LeetCode153. 寻找旋转排序数组中的最小值
    LeetCode152. 乘积最大子数组
    Python 异常值数据
    Python 生成周期性波动的数据 可指定数值范围
    VUE 组件 事件传递
    MyBatis-Plus And和Or的使用
    SQL Server 复制一条数据
    PLC扩展模拟量输入模块是什么
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14906052.html
Copyright © 2011-2022 走看看