NEFU大一省赛训练1(全题解)

目录

    • OP
    • A 哪些数不在
        • 思路
        • 代码
    • B 闹钟
        • 思路
        • 代码
    • C 减法
        • 思路
        • 代码
    • D 隧道
        • 思路
        • 代码
    • E 尖括号
        • 思路
        • 代码
    • F 恐龙的故事
        • 思路
        • 代码
    • G 序列和
        • 思路
        • 代码
    • H 巧克力
        • 思路
        • 代码
    • ED

OP

一个半小时七道题,两个小时一道题555;
G题鲨我;

A 哪些数不在

差分

思路

即在区间左端点标记+1,右端点标记-1;

遍历所有可行范围,遇到+1即计数值+1,-1亦然;

处理好开闭问题后,计数值为0的位置即没有被线段覆盖;

代码

#include
using namespace std;
typedef long long ll;

int main()
{
     
    int a[102],n,m,l,r,i;
    while(~scanf("%d",&m))
    {
     
        memset(a,0,sizeof a);
        scanf("%d",&n);
        while(m--)
        {
     
            scanf("%d%d",&l,&r);
            a[l-1]++,a[r]--;
        }
        queue<int>que;
        int c=a[0];
        for(i=1;i<=n;i++)
        {
     
            if(!c)que.push(i);
            c+=a[i];
            //printf("*");
        }
        printf("%d\n",que.size());
        while(que.size())
        {
     
            printf("%d%c",que.front(),que.size()-1?' ':'\n');
            que.pop();
        }
    }
    return 0;
}

B 闹钟

gcd

思路

要求从第一个工作开始时间开始,所有工作开始时间均响铃,即求所有工作开始时间与第一次工作开始时间的差的最大公倍数;

再遍历所有可行响铃间隔,找出第一个满足条件的即可;

代码

#include
using namespace std;
typedef long long ll;

int main()
{
     
    int n,m,i;
    ll del=0,g,mk;
    cin>>n>>m;
    cin>>mk;
    for(i=1;i<=n-1;i++)
    {
     
        scanf("%lld",&g);
        g-=mk;
        if(!del)del=g;
        else del=__gcd(del,g);
    }
    for(i=1;i<=m;i++)
    {
     
        scanf("%lld",&g);
        if(del%g==0)break;
    }
    if(i<=m)printf("YES\n%d\n",i);
    else printf("NO\n");
    return 0;
}

C 减法

思路

对于这个数据量,模拟的话大概会爆TLE,于是我打算记录累计减去的值;

在代码中,这个值即为del;
原题中所求的最小非零值即为最小的大于del的值;

再累加减去的值即可(注意每次减去的值不是找到的数本身,而是找到的数减去该次del值);

代码

#include
using namespace std;
typedef long long ll;

int main()
{
     
    ll del=0,a[100005]={
     0};
    int n,k,i;
    cin>>n>>k;
    for(i=1;i<=n;i++)
    {
     
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);
    //printf("%d\n",&a[3]);
    while(k--)
    {
     
        ll *p=upper_bound(a+1,a+n+1,del);
       // printf("%d %d %lld\n",p,a+n+1,del);
        if(p==a+n+1)printf("0\n");
        else printf("%lld\n",*p-del),del+=*p-del;
    }
    return 0;
}

D 隧道

bfs

思路

大致估计了一下,对于目标块每一个点进行 n 2 n^2 n2遍历时间复杂度也遭得住,也没想到比较好的办法进一步降低时间复杂度,于是做得比较暴力;

对于起始点进行正常的bfs形成起始块,如果目标点已达,则输出0;
否则对目标点bfs,再对连通块中的每一个点算出距离起始块对应最近点的距离;

os:竟然在字符串处理上栽了一发;

代码

#include
using namespace std;
typedef long long ll;
int c=1,n;//这道题代码贼乱,有两个bfs函数和一个求距离函数
int mp[51][51]={
     0},gt[51][51]={
     0};
void bfs(int xi,int xj)
{
     
    int di[]={
     0,0,1,-1},dj[]={
     1,-1,0,0};
    gt[xi][xj]=c;
    int ct=1;
    queue<pair<int ,int> >que;
    que.push({
     xi,xj});
    while(!que.empty())
    {
     
        pair<int,int>now=que.front();
        que.pop();
        for(int k=0;k<4;k++)
        {
     
            if(now.first+di[k]>=1&&now.first+di[k]<=n
                &&now.second+dj[k]>=1&&now.second+dj[k]<=n
                &&mp[now.first+di[k]][now.second+dj[k]]
                &&!gt[now.first+di[k]][now.second+dj[k]])
            que.push({
     now.first+di[k],now.second+dj[k]})
            ,gt[now.first+di[k]][now.second+dj[k]]=c,ct++;
        }
    }
    c++;
    //printf("%d\n",ct);
}
ll gdis(int xi,int xj)
{
     
    ll md=100005;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
     
        if(gt[i][j]==1)
        {
     
            md=min(md,(ll)(xi-i)*(xi-i)+(xj-j)*(xj-j));
        }
    }
    return md;
}
ll bfs1(int xi,int xj)
{
     
    int di[]={
     0,0,1,-1},dj[]={
     1,-1,0,0};
    gt[xi][xj]=c;
    int ct=1;
    queue<pair<int ,int> >que;
    que.push({
     xi,xj});
    ll mdis=gdis(xi,xj);
    while(!que.empty())
    {
     
        pair<int,int>now=que.front();
        que.pop();
        for(int k=0;k<4;k++)
        {
     
            if(now.first+di[k]>=1&&now.first+di[k]<=n
                &&now.second+dj[k]>=1&&now.second+dj[k]<=n
                &&mp[now.first+di[k]][now.second+dj[k]]
                &&!gt[now.first+di[k]][now.second+dj[k]])
            que.push({
     now.first+di[k],now.second+dj[k]})
            ,gt[now.first+di[k]][now.second+dj[k]]=c
            ,mdis=min(mdis,gdis(now.first+di[k],now.second+dj[k])),ct++;
        }
    }
    c++;
    //printf("%d\n",ct);
    return mdis;
}
int main()
{
     
    int fi,fj,ti,tj,i,j;
    //char g;
    cin>>n;
    cin>>fi>>fj>>ti>>tj;
    string g;
    for(i=1;i<=n;i++)
    {
     
        cin>>g;
        for(j=0;j<n;j++)
        {
     
            if(!(g[j]-'0'))mp[i][j+1]=1;
        }
    }
    bfs(fi,fj);
    if(gt[ti][tj])
    {
     
        printf("0");
    }
    else
    {
     
        ll ans=bfs1(ti,tj);
        printf("%lld",ans);
    }
    return 0;
}

E 尖括号

思路

对于一个尖括号串,最左侧的>右面的所有符号均可以被其消去,操作后此符号在最右端,最右侧的<同理;

接下来为了仅剩一个符号,我们需要统计要使 以上符号出现在在对应的端点 所需要删除的符号数并取最小值;
(即对于<<<<>>><><<>,若使最左侧的>出现在左端,则需要删去左侧的四个<,若使最右侧的<出现在最右端,需要删去右侧的一个>,故最小删去数为1)

代码

#include
using namespace std;
typedef long long ll;

int main()
{
     
    int t,n,i;
    string g;
    cin>>t;
    while(t--)
    {
     
        int n;
        cin>>n>>g;
        if(n==1)printf("0\n");
        else
        {
     
            int dl=0,dr=0;
            for(i=0;g[i];i++)
            {
     
                if(g[i]=='<')dl++;
                else break;
            }
            for(i=n-1;i>=0;i--)
            {
     
                if(g[i]=='>')dr++;
                else break;
            }
            printf("%d\n",min(dl,dr));
        }
    }
    return 0;
}

F 恐龙的故事

素数筛,模拟

思路

模拟即可

代码

#include
using namespace std;
typedef long long ll;
int p[10000007] , num[10000007] , pp[10000007];
int main()
{
     
    int c=0,cc=0;
    num[0]=1,num[1]=1;
    for(int i=2;i<=10000000;i++)
    {
     
        if(!num[i])p[c++]=i;
        for(int j=0;j<c&&i*p[j]<10000000;j++)
        {
     
            num[i*p[j]]=1;
            if(i%p[j]==0)break;
        }
    }
    for(int i=0;i<c;i++)
    {
     
        if(!num[i+1])pp[cc++]=p[i];
    }
    //for(int i=0;i
    int t,m;
    cin>>t;
    while(t--)
    {
     
        cin>>m;
        int *p=lower_bound(pp,pp+cc,m);
        printf("%d\n",*p);
    }
    return 0;
}

G 序列和

思路

这个题的描述有一些些些些问题,实际上需要输出的的是以x+y为第一排序依据升序排序,以x为第二排序依据降序排序之后的第一个;

为方便描述我们将删除序列中一个元素后的序列和称为缺刻和;
对于所有的缺刻和,我们只需要记录能满足该缺刻和的所有序列中,序列号最小的两个序列号;

在对所有缺刻和寻找符合要求的x+y即可;

注:这道题个人既被卡过空间,又被卡过时间,经测试,unordered_map所用空间比map少1/10左右;
用时:1077ms/2000ms,空间占用:60236k/65535k

代码

#include
using namespace std;
typedef long long ll;

unordered_map<int,pair<int,int> >mp;
unordered_map<int,pair<int,int> >::iterator it1;

int main()
{
     
    int g[200005]={
     0};
    int k,n;
    cin>>k;
    
    for(int i=1;i<=k;i++)
    {
     
        scanf("%d",&n);
        int sum=0;
        for(int j=1;j<=n;j++)
        {
     
            scanf("%d",&g[j]);
            sum+=g[j];
        }
        for(int j=1;j<=n;j++)
        {
     
            if(!mp[sum-g[j]].first)mp[sum-g[j]].first=i;
            else if(!mp[sum-g[j]].second&&mp[sum-g[j]].first!=i)mp[sum-g[j]].second=i;//避免我重我自己
        }
    }
    int f=0,mx=k+1,my=k+1;
    for(it1=mp.begin();it1!=mp.end();it1++)
    {
     
        if(it1->second.first&&it1->second.second)//两个均有值才参与排序
        {
     
            int a=it1->second.first;
            int b=it1->second.second;
            if(a+b<mx+my||(a+b==mx+my&&b<my))//注意第二排序依据
            {
     
                mx=min(a,b),my=max(a,b);
            }
        }
    }
    if(mx!=k+1)printf("YES\n%d %d",mx,my);//mk被更新过
    else printf("NO");
    //printf("\n%d",mp.size());
    return 0;
}

H 巧克力

模拟

思路

按要求模拟即可

代码

#include
using namespace std;
typedef long long ll;

int main()
{
     
    int t;
    cin>>t;
    while(t--)
    {
     
        int s,a,b,c;
        cin>>s>>a>>b>>c;
        ll ans=0;
        ans=s/c;
        ans+=b*(ans/a);
        printf("%lld\n",ans);
    }
    return 0;
}

ED

\

你可能感兴趣的:(比赛与题集)