zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:导弹袭击(数学+凸包+单调栈)

    题目背景

    $Guess$准备向敌军阵地发起进攻了!$Guess$的武器是自动制导导弹。
    然而在机房是不允许游戏的,所以班长$XZY$对游戏界面进行了降维打击,结果。。。


    题目描述

    众所周知,环境因素对导弹制导的效率影响是很大的。地图上总共有两种环境,暂且称为环境一和环境二。$Guess$有$n$种型号的导弹,每一种都有两个参数$a_i,b_i$,分别表示该型号导弹在两种环境下的恒定飞行速度。
    然而,$Guess$攻击的距离和角度会经常调整,导弹预定轨迹上的地形也会随之变化。一维化以后,导弹预定轨迹可以看成一条长度不定的线段,其中经过环境一的长度为$A$,经过环境二的长度为$B$。
    在不确定两种环境长度比重的情况下,$Guess$想知道,有哪几种型号的导弹是有用的。某种型号的导弹有用,当且仅当存在一对正实数$A,B$,使得该型号的总飞行时间为所有型号中最短的之一。

    原题见:$[CF535E]Tavas and Pashmaks$


    输入格式

    第一行一个正整数$n$,表示型号总数。
    接下来$n$行,第$i$行两个正整数$a_i,b_i$,分别表示$i$型导弹在环境一和环境二下的飞行速度。


    输出格式

    一行若干个正整数,从小到大输出每个有用的导弹型号的编号$i$,用单个空格隔开。


    样例

    样例输入:

    7
    6 6
    5 7
    3 13
    4 12
    2 13
    12 4
    3 13

    样例输出:

    1 3 4 6 7


    数据范围与提示

    样例解释:

    取$A=12,B=12$时,型号$1,4,6$均用时最快,为$4$单位时间;
    取$A=3,B=39$时,型号(参数相同)均用时最快,为$4$单位时间;
    容易发现无论如何调整$A,B$,型号$2,5$用时均不是最快的(注意$A,B$为正实数)。

    数据范围:

    对于$10\%$的数据,$nleqslant 2$;
    对于$20\%$的数据,$nleqslant 3$;
    对于$40\%$的数据,$nleqslant 300$;
    对于另$20\%$的数据,$nleqslant 3 imes 10^4,a_i,b_ileqslant 3 imes 10^3$;
    对于另$20\%$的数据,$nleqslant 3 imes 10^5,a_i,b_ileqslant 3 imes 10^4$;
    对于$100\%$的数据,$nleqslant 3 imes 10^5,a_i,b_ileqslant 10^9$。


    题解

    可以将每颗导弹看成是平面上的点$(frac{1}{a_i},frac{1}{b_i})$,这样问题就转化成了:给定$A,B$,最小化$z=Ax+By$($x,y$为顶点坐标)。

    那么,每两个点都会形成一个一次函数,而对于所有一次函数,位于其下凸包上的点也就是能做贡献的点。

    先将其按横坐标排序,再用一个单调栈维护这个下凸包就好了。

    注意如果有两点斜率为正,那么位于右边的点肯定不会做贡献。

    这道题卡了精度,所以算斜率的式子需要做如下化简:

    $Large egin{array}{rl} & frac{frac{1}{b_j}-frac{1}{b_i}}{frac{1}{a_j}-frac{1}{a_i}} \ =& frac{frac{b_i-b_j}{b_ib_j}}{frac{a_i-a_j}{a_ia_j}} \ =& frac{a_ia_j(b_i-b_j)}{b_ib_j(a_i-a_j)}end{array}$

    这样就可以避免精度问题。

    时间复杂度:$Theta(nlog n)$($log$来自排序)。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int a,b,id;}e[300001];
    int n;
    bool vis[300001],ans[300001];
    int sta[300001];
    bool cmp(rec a,rec b){return a.a>b.a||(a.a==b.a&&a.b>b.b);}
    double slope(rec a,rec b){return 1.0*a.a*b.a*(a.b-b.b)/(1.0*a.b*b.b*(a.a-b.a));}
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&e[i].a,&e[i].b);
    		e[i].id=i;
    	}
    	sort(e+1,e+n+1,cmp);
    	int maxb=e[1].b;
    	for(int i=2;i<=n;i++)
    		if(e[i].b<=maxb)vis[i]=1;
    		else maxb=e[i].b;
    	sta[++sta[0]]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(vis[i])continue;
    		if(slope(e[i],e[sta[sta[0]]])>0)continue;
    		while(sta[0]>1&&slope(e[i],e[sta[sta[0]]])<slope(e[sta[sta[0]-1]],e[sta[sta[0]]]))sta[0]--;
    		sta[++sta[0]]=i;
    	}
    	for(int i=1;i<=sta[0];i++)
    	{
    		ans[e[sta[i]].id]=1;
    		for(int j=sta[i]+1;j<=n&&e[sta[i]].a==e[j].a&&e[sta[i]].b==e[j].b;j++)ans[e[j].id]=1;
    	}
    	for(int i=1;i<=n;i++)if(ans[i])printf("%d ",i);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    CF1359D Yet Another Yet Another Task
    【数据结构】fhq_treap
    AtCoder Beginner Contest 182 题解
    UVA11992 Fast Matrix Operations
    双指针例题
    python使用国内镜像库
    APP元素定位工具之——Weditor
    安卓ADB的常见命令的使用
    函数进阶之迭代器,递归
    函数基础之对象,嵌套,名称空间和作用域
  • 原文地址:https://www.cnblogs.com/wzc521/p/11686095.html
Copyright © 2011-2022 走看看