• 模拟75 题解


    A. 导弹袭击

    想到了凸包,推出了题中的式子,然而没有继续推下去。

    或者说,高考数学线性规划没有学好。

    一种导弹的飞行时间函数为:

    $z_i=A*frac{1}{a_i}+B*frac{1}{b_i}$

    似乎显然是一个线性规划式,设$x=frac{1}{a_i},y=frac{1}{b_i}$。

    那么有$z=A*x+B*y$

    $y=frac{z}{B}-frac{A}{B}*x$

    要使$z$取得最小值,因为斜率是负的,只要维护这些点形成的左下凸包。

    将所有点的横坐标排个序,考虑栈顶和栈顶下的元素,就可以直接用单调栈插入,维护凸包。

    B. 炼金术士的疑惑

    显然用高斯消元。

    然而字符串读入很恶心,加上高斯消元差不多忘光了,打了很久。

    最终因为输出-0.0挂95分。

    我的思路是设每个化学方程式在最终化学方程式中出现的系数为$x_i$,

    那么根据物质列出方程,可以解出每个系数$x_i$,直接乘上$h_i$就可以搞最终答案。

    然而如果这些方程中存在一些方程可以被其他方程表示。

    那么可能会出现的情况是:变量数$n$大于方程数$m$,无法解出任何一个变量$x_i$。

    如果数据保证有解,出现这种情况,一定意味着有一些无意义的变量。

    也就是说不一定每一个方程$i$都要分配变量$i$。

    所以魔改一下高斯消元的方法就可以了。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<map>
     5 #define ull unsigned long long
     6 using namespace std;
     7 const ull p=131;
     8 const double eps=1e-8;
     9 map<ull,int> mp;
    10 int n,cnt;
    11 char s[3000];
    12 double val[205],x[205][205],ans[205];
    13 inline double read_double(){
    14     double x=0,t=0,tmp=1;
    15     int l=1,f=0;
    16     char ch=s[l++];
    17     while(!isdigit(ch)) f=ch=='-',ch=s[l++];
    18     while(isdigit(ch)) x=x*10+(ch^48),ch=s[l++];
    19     if(ch=='.'){
    20         ch=s[l++];
    21         while(isdigit(ch)) t=t*10+(ch^48),ch=s[l++],tmp/=10;
    22     }
    23     return f?-x-t*tmp:x+t*tmp;
    24 }
    25 inline ull hashstring(){
    26     ull ans=0;
    27     for(int i=1;s[i];++i) ans=ans*p+s[i]+1;
    28     return ans;
    29 }
    30 inline void read(int k){
    31     while(1){
    32         scanf("%s",s+1);
    33         if(s[1]=='=') break;
    34         if(s[1]=='+') continue;
    35         double t=read_double();
    36         scanf("%s",s+1);
    37         ull hsh=hashstring();
    38         if(!mp[hsh]) mp[hsh]=++cnt;
    39         int id=mp[hsh];
    40         x[id][k]+=t;
    41     }
    42     while(1){
    43         scanf("%s",s+1);
    44         if(s[1]=='H'&&s[2]=='=') break;
    45         if(s[1]=='+') continue;
    46         double t=-read_double();
    47         scanf("%s",s+1);
    48         ull hsh=hashstring();
    49         if(!mp[hsh]) mp[hsh]=++cnt;
    50         int id=mp[hsh];
    51         x[id][k]+=t;
    52     }
    53     if(k!=n+1) scanf("%lf",&val[k]);
    54 }
    55 inline double _abs(double x){
    56     return x>0?x:-x;
    57 }
    58 int bl[205];
    59 inline void gauss(){
    60     int i=1,p=1;
    61     while(i<=n&&p<=cnt){//第i个元 第p个方程
    62         int mxp=p;
    63         for(int j=p+1;j<=cnt;++j)//共有cnt个方程
    64             if(_abs(x[j][i])>_abs(x[mxp][i])) mxp=j;
    65         if(_abs(x[mxp][i])<eps){
    66             ++i; continue;
    67         }
    68         bl[p]=i;
    69         for(int j=i;j<=n+1;++j) swap(x[p][j],x[mxp][j]);
    70         for(int j=p+1;j<=cnt;++j){
    71             double f=x[j][i]/x[p][i];
    72             for(int k=i;k<=n+1;++k) x[j][k]-=f*x[p][k];
    73         }
    74         ++i; ++p;
    75     }
    76     for(int i=n;i;--i){
    77         for(int j=bl[i]+1;j<=n;++j) x[i][n+1]-=x[i][j]*ans[j];
    78         if(_abs(x[i][bl[i]])>eps) ans[bl[i]]=x[i][n+1]/x[i][bl[i]]; 
    79         else ans[bl[i]]=0;
    80     }
    81 }
    82 int main(){
    83     //freopen("knight1.in","r",stdin);
    84     //freopen("no.out","w",stdout);
    85     scanf("%d",&n);
    86     for(int i=1;i<=n+1;++i) read(i);
    87     gauss();
    88     double ret=0;
    89     for(int i=1;i<=n;++i) ret+=ans[i]*val[i];
    90     printf("%.1lf
    ",ret+eps);
    91     return 0;
    92 }
    B. 炼金术士的疑惑

    增加最后一个方程,用前面的方程消掉最后一个方程的未知量,

    于是最后剩下的就是要求的$h$。

    仍然需要一些奇怪的方法来保证每个元都被消掉,或许在一定意义上和我的做法有些异曲同工之妙。

    C. 老司机的狂欢

    考场上成功转化奇怪的题意:

    显然最多同时存在的老司机个数随着$time$单调。

    于是二分出一个$time$值,对于不符合条件的一组老司机连边。

    于是最多同时存在的老司机数转化为图的最大独立集。

    然而这似乎是一个$NPC$问题,于是死掉了。

     

    当时确实想了利用本题的一些特殊性质,比如边的一些特殊性质,然而没有想到。

    事实上确实是这样的。

    可以将所有的老司机按$x_i$排序,求出时间$time$之后的最终态,设为$y_i$。

    只要$y$值单调上升,就可以满足要求,于是问题转化为$LIS$问题。

    于是可以二分出答案。

    最后的方案构造方法比较难想:

    如果每一步都选择最优的转移点,不妨加入点0,那么$LIS$的转移过程中一定可以形成一棵树。

    考虑如何选择最优转移点,

    首先一定选择深度最大的(即dp值最大),

    对于深度相同的点,因为我们最终的字典序是排序后的,所以我们实际关注的是不同的最小值。

    于是在每次转移时都将转移点设为该点的父亲,倍增处理出$2^k$级父亲,和这条链上的最小值。

    对于深度相同的两个点$a$,$b$,只要求出两个点分别到$lca$链上的最小值,就可以比较。

    可以用树状数组维护最优二元组,进行转移。

  • 相关阅读:
    JavaScript高级程序设计之动态脚本及动态样式
    JavaScript高级程序设计之location对象
    JavaScript高级程序设计之window对象
    JavaScript高级程序设计之寄生组合式继承
    JavaScript高级程序设计之原型对象
    编写可维护的JavaScript之简易模版
    编写可维护的JavaScript之事件处理
    JavaScript 编码风格指南
    JavaScript高级程序设计之基本包装类型
    JavaScript高级程序设计之函数
  • 原文地址:https://www.cnblogs.com/skyh/p/11684072.html
Copyright © 2020-2023  润新知