HGOI20191115 模拟赛 题解

Problem A 表演

有$n$个有点权的点,$m$个有边权的边。对于每个点$u$,输出从这个点出发到$v$,其路径权值的两倍加上v的点权和最小的值。

对于$100\%$的数据,满足$1 \leq n,m \leq 2\times 10^5 $

Solution : 

  考虑一个简单的转移,$f[u]$表示点$u$的最小答案,最初$f[u]$ 为$u$的点权。

  每一次更新,就是相邻的两个点$u,v$之间,用边权的两倍来更新答案。

  如果在图上DP,那么就相当于,将初始这些点权加入priority_queue,跑最短路即可。

  时间复杂度为$O(m log_2 n)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=2e5+10;
struct rec{ int pre,to,w;}a[N<<1];
int n,m,tot;
bool inq[N];
int head[N],d[N],val[N];
namespace Fast_IO {
    inline int read() {
        int x=0,w=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return w?-x:x;
    }
    void write(int x) {
        if (x<0) x=-x,putchar('-');
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void writeln(int x) {
        write(x); putchar('\n');
    }
};
using namespace Fast_IO;
void adde(int u,int v,int w) {
    a[++tot].pre=head[u];
    a[tot].to=v;
    a[tot].w=w*2;
    head[u]=tot;
}
struct Node {
    int id,val;
};
struct cmp {
    bool operator () (Node a,Node b) {
        return a.val > b.val;   
    }
};
priority_queue<Node,vector<Node>,cmp>q;
void dijkstra() {
    for (int i=1;i<=n;i++) {
        d[i]=val[i];
        inq[i]=true;
        q.push((Node){i,val[i]});
    }
    while (q.size()) {
        int u=q.top().id; q.pop(); inq[u]=false;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to; 
            if (d[v]>d[u]+a[i].w) {
                d[v]=d[u]+a[i].w;
                if (!inq[v]) q.push((Node){v,d[v]}); 
            }
        }
    }
}
signed main() {
    n=read();m=read(); 
    for (int i=1;i<=m;i++) {
        int u=read(),v=read(),w=read();
        adde(u,v,w); adde(v,u,w);
    }
    for (int i=1;i<=n;i++) val[i]=read();
    dijkstra();
    for (int i=1;i<n;i++) write(d[i]),putchar(' ');
    writeln(d[n]);
    return 0;
}
exciting.cpp

Problem B 逮虾户

 要求的车速是一个大于等于0的值,设第$i$次估计的车速为$v_i$,走的路程为$s_i$,

  设$v'$表示真实速度,则有偏差值$d$的定义式为 $d = v' - v_i$。

  考虑对速度的估计的偏差值d每一次都恒定。

  给出$n$次测算的结果$s_i , v_i$,和这几次测试总共的用时$t$,输出$d$的值。

  对于$100\%$的数据满足$1 \leq n\leq 10^3,1 \leq s_i \leq 10^3 , |v_i| \leq 10^3$

Solution :

  考虑$t$这个函数是在$n+1$段不连续的定义域中单调减的。

  所以,我们可以做$n+1$二分,就能找到些定义域中和d最相近的。

、 注意,最后统计答案的时候,需要check一下实际车速大于等于0.

  时间复杂度为$O(n ^2 log_2 S)$,其中$S$表示确定实数域大小。

# include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const double eps = 1e-9;
struct rec{double s,v;}a[N];
int n,t;
vector<double>ans;
bool cmp (rec x, rec y) {
    return x.v>y.v;
}
double f (double x) {
    double res = 0;
    for (int i=1;i<=n;i++) res += (double)a[i].s/(double)(x+a[i].v);
    return res;
}
double work(double l,double r) {
    while (fabs(r-l)>=eps) {
        double mid = (l+r)/2.0;
        if (f(mid)<=t) r=mid;
        else l=mid; 
    }
    return l;
}
double calc(double x) {
    return fabs(f(x)-t);
}
int main() {
    scanf("%d%d",&n,&t);
    for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].s,&a[i].v);
    sort(a+1,a+1+n,cmp);
    double l=-1e9;
    for (int i=1;i<=n;i++) {
        ans.push_back(work(l,-a[i].v-eps));
        l=-a[i].v+eps;
    }
    ans.push_back(work(l,1e9));
    double res = ans[0], delta =calc(ans[0]);
    for (int i=1;i<ans.size();i++) {
        bool ok = true;
        for (int j=1;j<=n;j++) if (a[j].v+ans[i]<eps) {
            ok = false; break;
        }
        if (!ok) continue;
        double tmp = calc(ans[i]);
        if (tmp <= delta) {
            res = ans[i]; delta = tmp;
        }
    }
    printf("%.10lf\n",res);
    return 0;
}
dejavu.cpp

Problem C 跳一跳

  如果序列中任意相邻两个元素,都是极值点,其中一个是极大值点,另外一个极小值点。

   那么这个序列叫做波浪序列。

  给出对于长度为$n$序列,输出最长波浪序列的长度。

  对于$100\%$的数据满足 $1 \leq n \leq 10^5 , 1 \leq a_i \leq 10^9 $

Solution :

  由于元素只进行比较,所以我们可以将其离散化。

  设$f[i][0]$表示第$i$个元素作为极小点为结尾的最长波浪序列长度,$f[i][1]$表示第$i$个元素作为极大点为结尾的最长波浪序列长度。

  转移方程为:$f[i][0] = max(f[j][1] + 1) (a_i < a_j) , f[i][1] = max (f[j][0] + 1) (a_i > a_j)$

  可以用值域线段树来维护这个$DP$。时间复杂度为$O(n log_2 n)$

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],n,f[N][2];
vector<int>tmp;
namespace Fast_IO {
    inline int read() {
        int x=0,w=0; char c=0;
        while (c<'0'||c>'9') w|=c=='-',c=getchar();
        while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return w?-x:x;
    }
    void write(int x) {
        if (x<0) x=-x,putchar('-');
        if (x>9) write(x/10);
        putchar('0'+x%10);
    }
    void writeln(int x) {
        write(x); putchar('\n');
    }
};
using namespace Fast_IO;
struct Segment_Tree {
    int tr[N<<2];
    # define ls (x<<1)
    # define rs (x<<1|1)
    # define mid (l+r>>1)
    # define lson ls,l,mid
    # define rson rs,mid+1,r
    void update(int x,int l,int r,int pos,int opx) {
        if (l==r) {
            tr[x]=max(tr[x],opx);
            return;
        }
        if (pos<=mid) update(lson,pos,opx);
        else update(rson,pos,opx);
        tr[x]=max(tr[ls],tr[rs]);
    }
    int query(int x,int l,int r,int opl,int opr) {
        if (opl<=l && r<=opr) return tr[x];
        int ret=0;
        if (opl<=mid) ret=max(ret,query(lson,opl,opr));
        if (opr>mid) ret=max(ret,query(rson,opl,opr));
        return ret;
    }
}tr0,tr1;
int main() {
    
    n=read();
    for (int i=1;i<=n;i++) {
        tmp.push_back(a[i]=read());
    }
    sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
    int m = tmp.size();
    for (int i=1;i<=n;i++) {
        a[i]=lower_bound(tmp.begin(),tmp.end(),a[i])-tmp.begin()+1;
    }
    int ans = 0;
    for (int i=1;i<=n;i++) {
        f[i][0]=f[i][1]=1;
        if (a[i]-1>=1) f[i][1] = max(f[i][1],tr0.query(1,1,m,1,a[i]-1)+1);
        if (a[i]+1<=m) f[i][0] = max(f[i][0],tr1.query(1,1,m,a[i]+1,m)+1);
        ans = max(ans,max(f[i][0],f[i][1]));
        tr0.update(1,1,m,a[i],f[i][0]);
        tr1.update(1,1,m,a[i],f[i][1]);
    }
    writeln(ans);
    return 0;
}
jump.cpp
posted @ 2019-11-15 14:53  ljc20020730  阅读(148)  评论(0编辑  收藏  举报