入门赛5

前言

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

本赛涉及题目:
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 50
long 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 0x7f7f7f7f
using 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 long
using 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 long
using 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的。。。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值