zoukankan      html  css  js  c++  java
  • 20210716考试-NOIP16

    考场时Prim的 $i$ 写成 $k$ 100->0 rank1->rank23


    T1 Star Way To Heaven

    考场正解:假设你要二分答案,则几个圆组成几道“屏障”把画面切成几部分,走每一个屏障的最长边的中点,这样是最优的。

    但是屏障间的点可能对答案有影响,所以要把它们合成为一道屏障。

    首先取上或下边界,如集合,之后每次取离集合最近的点加入集合,这样就可以找到“屏障”合成后的样子,因为:

    对于这样子的三个点,$d(1,2),d(2,3)<=d(1,3)$,所以对于题目而言,只使用 $d(1,2),d(2,3)$ 的距离就可以成功限制好对答案的影响。

    在求出的点集中的边取最大值的一半即为答案。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1100000,K=6100;
    const long double INF=0x7fffffff;
    inline int read()
    {
        int s=0,w=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
        while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
        return s*w;
    }
    int n,m,k;
    bool vis[K];
    long double x[K],y[K],ans,dis[K];
    long double X(int q)
    {
        return x[q];
    }
    long double Y(int q)
    {
        return y[q];
    }
    long double D(int a,int b)
    {
        return sqrt((X(a)-X(b))*(X(a)-X(b))+(Y(a)-Y(b))*(Y(a)-Y(b)));
    }
    int main()
    {
        n=read();m=read();k=read();
        for(int i=1;i<=k;i++)
        {
            scanf("%Lf%Lf",&x[i],&y[i]);
        }
        for(int i=1;i<=k;i++)
        {
            dis[i]=1.0*y[i];
        }
        dis[k+1]=m;
        for(int i=1;i<=k+1;i++)
        {
            long double minn=INF;
            int x=-1;
            for(int j=1;j<=k+1;j++)
            {
                if(vis[j])
                    continue;
                if(dis[j]<minn)
                {
                    minn=dis[j];
                    x=j;
                }
            }
            vis[x]=1;
            ans=max(dis[x],ans);
            if(x==k+1)
            {
                printf("%.8Lf
    ",ans*0.5);
                return 0;
            }
            for(int j=1;j<=k;j++)
            {
                if(!vis[j])
                {
                    dis[j]=min(dis[j],max(dis[x],D(x,j)));
                }
            }
            dis[k+1]=min(dis[k+1],m-y[x]);
        }
    //    printf("%.8Lf
    ",ans/2.0);
        return 0;
    }
    /*
    10 5 2
    1 1
    2 3
    */

    T2 God Knows

    玄学DP,考场想50mins没想出 $n^2$ DP,写了一个只能过样例的假DP竟然有10分...

    我对这个DP理解并不深,这篇博客更好。

    egin{align} f[i]=min f[j]+c[i] (j < iAnd p[j] < p[i] And forall j< k < i , p[k] < p[j] || p[k] > p[i] ) end{align}

    f[i]表示前i个点已经被解决。
    对这个式子的解释是:f[i]如果要从f[j]转移,则必须要保证 ([j+1,i]) 内的点都与线 (j) 或线 (i) 相交,同时线 (j) 不能与线 (i) 相交。
    对这个式子分析可以发现,从 (1)(n) 的一个合法的转移序列中, (p[i]) 是递减的。即 (p[i])(i) 上递减。
    或者说, (i)(p[i]) 上递减。
    那么可以以 (p) 维护一棵线段树,可以 (O(log(n))) 查出合法最佳转移。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=210000,INF=0x7fffffff;
    int nxt,n,p[N],w[N];
    struct xds
    {
        int l,r,mn,mx,upmn;
    }t[N*4];
    void build(int p,int l,int r){
        t[p].l=l; t[p].r=r;
        t[p].mn=t[p].upmn=INF;
        t[p].mx=-1;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
    }
    int calc(int p,int nxt)
    {
        if(t[p].l==t[p].r)
            return t[p].mx>nxt?t[p].mn:INF;
        if(t[p*2+1].mx>nxt)
            return min(t[p*2].upmn,calc(p*2+1,nxt));
        return calc(p*2,nxt);
    }
    int query(int p,int l,int r)
    {
        int res=INF;
        if(t[p].l>=l&&t[p].r<=r)
        {
            res=calc(p,nxt);
            nxt=max(t[p].mx,nxt);
            return res;
        }
        if(r>=t[p*2+1].l)
            res=min(query(p*2+1,l,r),res);
        if(l<=t[p*2].r)
            res=min(query(p*2,l,r),res);
        return res;    
    }
    void insert(int p,int pos,int tail,int val){
        if(t[p].l==t[p].r){
            t[p].mn=val;
            t[p].mx=tail;
            return ;
        }
        if(pos<=t[p*2].r) insert(p*2,pos,tail,val);
        else insert(p*2+1,pos,tail,val);
        t[p].mx=max(t[p*2].mx,t[p*2+1].mx);
        t[p].mn=min(t[p*2+1].mn,t[p*2].upmn=calc(p*2,t[p*2+1].mx));
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&p[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        n++;
        p[n]=n;
        build(1,0,n+1);
        insert(1,0,0,0);
        for(int i=1,ans;i<=n;i++)
        {
            nxt=-1;
            ans=query(1,0,p[i]-1)+w[i];
            insert(1,p[i],i,ans);
            if(i==n)
                printf("%d
    ",ans);
        }
        return 0;
    }

    T3 Lost My Music
    image
    这题首测卡题面没说的精度,大家纷纷炸成10分。
    考场暴力50分。

    作如下变形:
    (frac{c_v-c_u}{dis(u,v)}=-frac{c_u-c_v}{dep_u-dep_v})

    如果把 (c) 看成横坐标,(dep)看成纵坐标,题目变成了一个实时维护斜率最大值的题。
    维护一个下凸包,树上 (dfs),用单调栈维护凸包,每到达一个点,二分出该点在凸包上的插入位置,这时栈顶斜率即为该点答案。
    回溯时把凸包改回原来的样子。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=1100000;
    long double ans[N];
    int dep[N],head[N],cnt,zhan[N];
    int n;
    ll c[N];
    struct bian
    {
        int nxt,to;
    }b[N];
    void add(int u,int v)
    {
        b[++cnt].to=v;
        b[cnt].nxt=head[u];
        head[u]=cnt;
    }
    long double K(int x,int y)
    {
        return (long double)(c[x]-c[y])/(long double)(dep[x]-dep[y]);
    }
    int get(int u,int r)
    {
        int l=2,mid;
        long double k1,k2;
        while(l<=r)
        {
            mid=(l+r)>>1;
            k1=(long double)(-1)*K(zhan[mid],zhan[mid-1]);
            k2=(long double)(-1)*K(zhan[mid],u);
            if(k1<k2)
                r=mid-1;
            else
                l=mid+1;
        }
        return l-1;
    }
    void dfs(int u,int fa,int top)
    {
        int k=get(u,top)+1,tmp1=zhan[k],tmp2=zhan[k-1];
        if(u==1)
            k=1;
        ans[u]=(long double)(-1)*K(tmp2,u);
        zhan[k]=u;
        for(int i=head[u],v;i;i=b[i].nxt)
        {
            v=b[i].to;
            if(v==fa)
                continue;
            dep[v]=dep[u]+1;
            dfs(v,u,k);
        }
        zhan[k]=tmp1;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&c[i]);
        }
        for(int i=2,a;i<=n;i++)
        {
            scanf("%d",&a);
            add(a,i);
        }
        dep[1]=1; 
        dfs(1,0,0);
        for(int i=2;i<=n;i++) 
        {
            printf("%.10Lf
    ",ans[i]);
        }
        return 0;
    }
    /*
    8
    31516 11930 18726 12481 79550 63015 64275 7608
    1 1 2 4 2 4 5
    */

    这次考试用脚出题……

    下发文件上写着第一题没有SPJ,第二题有,实际相反。
    T1题面说输出一行整数……害了多少人以为是走整数点路径。
    T3卡精度后来又重测
    T3数据说的 (500) 内数据也残缺
    上一次考试T1还出了超出100%数据范围的数据,巨佬cyh搞了1h才发现。

  • 相关阅读:
    Java中Date和Calender类的使用方法
    看《做性能测试需要做些什么》
    【笔记】jquery append,appendTo,prepend,prependTo 介绍
    【实践】jquery实现滑动动画及轮播
    【实践】四联联动 + 更加优化
    【实践】jQuery实现三联联动
    关于导入excel报错的处理(xls,xlsx)
    递归算法及经典案例
    验证身份证真假
    正则表达式
  • 原文地址:https://www.cnblogs.com/HKHbest/p/15021203.html
Copyright © 2011-2022 走看看