入门赛5

来源:互联网 发布:c语言开发环境 编辑:程序博客网 时间:2024/06/05 16:35

  • 前言
  • T1 Curriculum Vitae
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T2 Math Show
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T3 Four Segments
    • 题面
    • 题意
    • 思路
    • 代码n2
    • 核心代码n3
    • 小结
  • T4 Monitor
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T5 Chemistry in Berland
    • 题面
    • 题意
    • 思路
    • 代码AC
    • 代码 树
    • 小结
  • T6 Random Query
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • 总结

前言

完全挂的一场比赛,差点就爆零了.

本赛涉及题目:
A: [Curriculum Vitae]

B: [Math Show]

C: [Four Segments]

D: [Monitor]

E: [Chemistry in Berland]

F: [Random Query]


T1 Curriculum Vitae

题面

原题地址: [A]

题意

有n个数,不能改变数的顺序,问最少删除几个数使这个数串没有0在1的右边,输出这个数串的最大可能长度.

思路

我有两种思路,虽然有一种一直WA…(然而我看着没区别)
AC思路:
求出每个点左边0的个数和右边1的个数,求这两数和的最大值,再加上1就是答案(1是所枚的点)

代码

#include<bits/stdc++.h>using namespace std;int a[105],i,j,n,ans=1,ans_;int main(){    for(scanf("%d",&n),i=0;i<n;i++) scanf("%d",&a[i]);    for(i=0;i<n;i++)    {        ans_=0;        for(j=0;j<=i;j++) if(a[j]==0) ans_++;        for(j=i;j<n;j++) if(a[j]==1) ans_++;        ans=max(ans,ans_);    }    printf("%d",ans);    return 0;}

小结

为什么我的第一个思路不行!!!(还是不要贴出来吧…)


T2 Math Show

题面

原题地址: [B]

题意

有n个任务,每个任务都有k个子任务,且每个任务的子任务是一样的,每个子任务完成后都获得一个点数,一个大任务完成额外获得一个点数,求在M时间里最多获得的点数.

思路

由于数据范围比较小,可以爆枚完成大任务的个数,然后求点数的最大值,具体见代码.

代码

#include<bits/stdc++.h>using namespace std;#define N 50long long n,k,M,ans,tot,now,ans_,d,i,j,e;int t[N+5];int main(){    for(scanf("%d%d%d",&n,&k,&M),i=0;i<k;i++) scanf("%d",&t[i]),tot+=t[i];//求完成一个大任务所需的时间;    sort(t,t+k);//初始化;    for(e=0;e<=n;e++)    {        now=tot*e;//完成e个大任务所需的时间;        if(now>M) break;//此时时间超限,以后也必定超限,故退出;        ans_ = (k+1)*e;//此时所获得的点数(大任务);        for(i=0;i<k;i++)        {            for(j=e+1;j<=n;j++)//将余下的时间分配到小任务里;            {                now+=t[i];                ans_++;                if(now>M)                {                    ans_--;//最后使时间超限的点数去掉;                    i=k+1;                    break;                }            }        }        ans=max(ans,ans_);//求最大值;    }    printf("%d",ans);    return 0;}

小结

扫一眼题面,我还以为这题不可做,然后就没写,没想到怎么水.


T3 Four Segments

题面

原题地址: [C]

题意

定义sum(x,y)为区间[a[x],a[y])的数字和,求三个点使sum(0,d1)-sum(d1,d2)+sum(d2,d3)-sum(d3,n) max. a数组是从零开始存数.

思路

求出前缀和的变式(a[i]存sum(0,i)),通过计算我们发现其实这个变式就是前缀和往后移以为,开头补0,这样可以用

for(i=1;i<=n;i++) scanf("%d",&a[i]),a[i]+=a[i-1]

来算前缀和,此时的sum(x,y)=a[y]-a[x];
原式=2*(a[d3]-a[d2]+a[d1])-a[n];
要使原式最小,只要使a[d3]-a[d2]+a[d1]最小,此时可以爆枚,但因为数据范围,n^3会TLE,于是又要加一步优化:
枚举d2的值,d3就是[a[d2],a[n]]的最大值的下标,d1就是[a[0],a[d2]]的最大值的下标,此时时间复杂度O(n^2).

代码#n^2

#include<bits/stdc++.h>#define inf 0x7f7f7f7fusing namespace std;long long ans=-inf,n,i,j,k,t[5007],d1,d2,d3,d1_,d2_,d3_,x,ans1,ans2,ans3;int main(){    for(scanf("%lld",&n),i=1;i<=n;i++)    {        scanf("%lld",&x);        t[i]=x+t[i-1];    }    for(d2_=0;d2_<=n;d2_++)    {        d2=d2_;d1=d3=d2;        for(d1_=0;d1_<=d2_;d1_++) if(t[d1_]>=t[d1]) d1=d1_;        for(d3_=d2_;d3_<=n;d3_++) if(t[d3_]>=t[d3]) d3=d3_;        if(t[d1]+t[d3]-t[d2]>=ans)        {            ans=t[d1]+t[d3]-t[d2];            ans1=d1;            ans2=d2;            ans3=d3;        }    }    printf("%lld %lld %lld",ans1,ans2,ans3);    return 0;}

核心代码#n^3

for(d1_=0;d1_<=n;d1_++){    for(d2_=d1_;d2_<=n;d2_++)    {        for(d3_=d2_;d3_<=n;d3_++)        {            if(t[d1_]+t[d3_]-t[d2_]>=ans)            {                ans=t[d1_]+t[d3_]-t[d2_];                d1=d1_;d2=d2_;d3=d3_;            }        }    }}

小结

重点是n^2的优化难想,我考完了才想到这种优化.


T4 Monitor

题面

原题地址: [D]

题意

有一个n*m的方阵,有些点会在某个时间坏掉,当坏掉的点组成一个k*k的方阵时,这个大方阵就变坏了,输出最小方阵变坏的时间,如果不可能变坏,输出-1。

思路

先将时间排序一下,每次更新ans,并将一个点置成坏,然后算一下有没有k*k的坏点。
如果爆枚k*k的方阵,但时间复杂度为q*n*m*k*k,直接爆
此时可以优化成每个点存其左上角k*k个点中坏掉的点的个数,当这个点到达k*k时,这个方阵就坏掉了。
此时每个点坏掉之后,向左下扩展一个k*k的方阵。
但这样时间复杂度为n^3*m,还是会TLE。
所以用另一种方式(抄的)(原理不会讲):

代码

#include<cstdio>#include<algorithm>using namespace std;struct Point{    int x,y,t;    bool operator<(const Point& b) const    {        return t<b.t;    }}p[255000];int b[510][510];bool c[510][510];int n,m,k,q;bool judge(int x,int y){    int i,ans=0;    for(i=x;i<=n;i++)    {        if(b[i][y]<k) break;        ans++;        if(ans>=k)  break;    }    for(i=x-1;i>=1;i--)    {        if(b[i][y]<k) break;        ans++;        if(ans>=k) break;    }    if(ans>=k) return true;    else return false;}int main(){    int i,j;    bool boo;    scanf("%d%d%d%d",&n,&m,&k,&q);    for(i=1;i<=q;i++)        scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].t);    sort(p+1,p+q+1);    for(i=1;i<=q;i++)    {        int& x=p[i].x;        int& y=p[i].y;        c[x][y]=true;        for(j=y;j>=1;j--)        {            if(!c[x][j]) break;            b[x][j]=b[x][j+1]+1;            if(b[x][j]>=k)            {                boo=judge(x,j);                if(boo==true)                {                    printf("%d",p[i].t);                    return 0;                }            }        }    }    printf("-1");    return 0;}

小结

其实还可以打二分,只是代码太长,懒得打。


T5 Chemistry in Berland

题面

原题地址: [E]

题意

有n种化学药品,拥有b1~bn,需要a1~an,有n-1条化学方程,第i个xi,ki代表ki个xi药品可以换成i+1药品,但1个i+1药品只能换成1个xi药品。
(此处的xi,i+1都是药品编号,i从1开始计数)

思路

刚开始是想建一棵数,从每个叶子节点一路换到树顶,再判断树顶,然后在第6个点WA了,因为是第6个点,所以我以为我代码错了,调了半天没调出来,于是又换了一种思路。
题目中有个条件1 ≤ x(j + 1 )≤ j,表示第i种药品只能转化成1~i-1种药品,于是就可以从尾部开始扫,再判断头部,此时还是WA6,看来不是我代码错了,再看一下数据范围,发现连long long 都能爆,所以改成double,然后就完美了。

代码AC

#include<bits/stdc++.h>#define N 100000#define LL long longusing namespace std;pair<LL,LL> change[N+5];LL x,n,i;double need[N+5];int main(){    for(scanf("%lld",&n),i=1;i<=n;i++) scanf("%lf",&need[i]);    for(i=1;i<=n;i++) scanf("%lld",&x),need[i]=x-need[i];    for(i=2;i<=n;i++) scanf("%lld%lld",&change[i].first,&change[i].second);    for(i=n;i>1;need[i]=0,i--) need[change[i].first]+=need[i]>0?1LL*need[i]*change[i].second:need[i];    need[1]>0?printf("NO"):printf("YES");    return 0;}

代码 树

//一直卡23点,不知道为什么,求各位dalao看看;#include<bits/stdc++.h>#define N 100000#define LL long longusing namespace std;struct Line{    int v,k;}line[N+5];long long head[N+5],away[N+5]/*入度计算*/,e,n,i,j,x,k;double tree[N+5],r;void add(int u,int v,int k){    line[e].v=v;    line[e].k=k;    head[u]=e;    e++;    away[v]++;}int main(){    memset(head,-1,sizeof(head));    scanf("%lld",&n);    for(i=1;i<=n;i++) scanf("%lf",&tree[i]);    for(i=1;i<=n;i++) scanf("%lf",&r),tree[i]-=r;    for(i=2;i<=n;i++)    {        scanf("%lld%lld",&x,&k);        add(i,x,k);//叶子指向根;    }    for(i=1;i<=n;i++)    {        if(!away[i])//判断叶子节点;        {            x=i;            while(x!=1)//往根走;            {                if(tree[x]>=0) tree[line[head[x]].v]+=tree[x];                else tree[line[head[x]].v]+=tree[x]*line[head[x]].k*1LL;                x=line[head[x]].v;            }        }    }    tree[x]>=0?printf("YES"):printf("NO");    return 0;}

小结

为什么第六个点就爆long long 了,出题人也太坑了吧,我还以为我代码错了呢!!!


T6 Random Query

题面

原题地址: [F]

题意

不会讲。

思路

只用计算的贡献

代码

#include<bits/stdc++.h>const int N=1e6;using namespace std;int i,head[N+5];long long ans;int main(){    for(scanf("%d",&n),i=1;i<=n;i++)    {        scanf("%d",&x);ans+=(long long)(i-head[x])*(n-i+1)*2;head[x]=i;    }    ans-=n;    printf("%.12lf",(double)ans/n/n);    return 0;}

小结

代码抄的~~


总结

e~~~,想法都是有的,代码都是WA的。。。

原创粉丝点击