HRBU 2021暑期训练解题报告阶段三Day2

目录

A - Jessica's Reading Problem

B - Bound Found

C - Subsequence

D - Tallest Cow

E - Straight Master

F - 非常男女

G - 矩形A + B


A - Jessica's Reading Problem

题意:

杰西卡·黄面临期末考试的严峻挑战,她平时并没有认真听课,但一个追求她的男生帮她画了范围和知识点,因此两人的关系更进一步。
现在书上一共有P页,每页上有一个关于a[i]的知识点,知识点可能重复。
请问杰西卡最少需要看几页就能复习完。

思路:

先用想办法计算出总共有多少种知识点。

之后便是尺取找最短的序列满足条件。

  • 考察点:尺取,思维

代码:

//卡输入,必须scanf或快读
//关闭输入流+"\n"还是会T
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e6+100;

map<int,int> mp;

int a[maxn];
int main()
{
    int n,k=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(mp[a[i]]==0) k++;
        mp[a[i]]=1;
    }
    mp.clear();
    int l=1,r=1;
    int cnt=0,len=inf;
    while(l<=n){
        while(r<=n&&cnt<k){
            if(mp[a[r]]==0) cnt++;
            mp[a[r++]]++;
        }
        if(cnt<k) break;
        len=min(len,r-l);
        mp[a[l]]--;
        if(mp[a[l++]]==0) cnt--;
    }
    printf("%d\n",len);
}

B - Bound Found

题意:

现在给你一个长度为n的序列a,a[i]<=10000。
接下来会有k次询问,每次询问会给出一个值t。
找到a的一个子序列使得这个子序列的和的绝对值尽可能地接近t。


输出这个子序列的序列和它的起始位置与终止位置。

思路:

首先求出1~r的所有的前缀和并进行存储。

接下来进行尺取,具体过程请看代码注释。

  • 考察点:尺取,排序,思维

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+500;
struct node
{
    int x,r;   ///x:前缀和  r代表:1~r之前的数的和为x
    node(){}
    node(int xx,int rr):x(xx),r(rr){}
};

bool cmp(node z,node c){
    return z.x<c.x;
}
node a[maxn];
int main()
{
    int n,k,t;
    ios::sync_with_stdio(false);
    while(cin>>n>>k&&n&&k){
        a[0].x=0;a[0].r=0;
        for(int i=1;i<=n;i++){
            cin>>a[i].x;
            a[i].x=a[i].x+a[i-1].x;
            a[i].r=i;
        }

        sort(a,a+1+n,cmp);
//        for(int i=1;i<=n;i++){
//            cout<<a[i].x<<" "<<a[i].r<<endl;
//        }
        for(int i=0;i<k;i++){
            cin>>t;
            int l=0,r=1;
            int finl,finr;
            int mint=inf;
            int ans;
            while(mint&&r<=n){        //mint=0 =>存在完全相符的答案,直接输出即可
                int chazhi=a[r].x-a[l].x;
                //cout<<a[r].r<<" "<<a[l].r<<" "<<chazhi<<" "<<mint<<endl;
                if(abs(chazhi-t)<=mint){
                    ans=chazhi;
                    finl=a[l].r;
                    finr=a[r].r;
                    mint=abs(chazhi-t);
                }
                if(chazhi>t) l++;
                if(chazhi<t) r++;
                if(l==r) r++;
            }
            if(finl>finr) swap(finl,finr);
            cout<<ans<<" "<<finl+1<<" "<<finr<<endl;
        }
    }
}

C - Subsequence

题意:

给定一个长为n的序列a,求a的一个最短子序列长度满足:

该子序列和>=S

思路:

裸的尺取,干就完了!

  • 考察点:尺取

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+500;
int a[maxn];
int main()
{
    int t,n,s;
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        cin>>n>>s;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        int l=1,r=1;
        int ans=inf;
        int sum=0;
        while(l<=n){
            while(sum<s&&r<=n){
                sum+=a[r++];
            }
            if(sum<s)
                break;
            ans=min(ans,r-l);
            sum-=a[l++];
        }
        if(ans==inf) cout<<"0\n";
        else cout<<ans<<"\n";
    }
}

D - Tallest Cow

题意:

FJ有N头牛,先给出最高的牛的编号和高度。(牛的高度为整数)


接下来会给出r组数据:
x y
代表x牛平视y牛,编号(x+1)~(y-1)的牛会比两边矮(矮多少不知道,反正就是矮)
请你最后给出每头牛最高是多少?

思路:

利用到差分数组的特性:处理区间增减问题,非常的简单。

  • 考察点:差分数组

  • 坑点:会有重复数据出现

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=1e4+500;
int a[maxn];
int main()
{
    int n,i,h,r,x,y;
    map<pair<int,int>,int> mp;
    cin>>n>>i>>h>>r;
    memset(a,0,sizeof(a));
    a[0]=h;
    for(int i=0; i<r; i++)
    {
        cin>>x>>y;
        if(x>y) swap(x,y);
        if(mp[make_pair(x,y)])
            continue;
        mp[make_pair(x,y)]=1;
        a[x+1]-=1;
        a[y]+=1;
//        for(int i=1; i<=n; i++)
//        {
//            if(i!=1)
//                cout<<" ";
//            cout<<a[i];
//        }
//        cout<<endl;
    }
    for(int i=1; i<=n; i++)
    {
        a[i]=a[i]+a[i-1];
    }
    for(int i=1; i<=n; i++)
    {
        if(i!=1)
            cout<<" ";
        cout<<a[i];
    }
    cout<<endl;
}

E - Straight Master

题意:

给你一个长为n的数组,每次你可以选择一个长为3,4,5的区间使其中的值减1。
问是否能把所有的数变成0。

思路:

利用差分数组特性:

对于数组a产生的差分数组b,使数组a的a[l]~a[r]全部减1=>b[l]-1  b[r+1]+1

接下来是关键:

  1. b[1]==a[1],所以b[1]一定为正数。
  2. b[2],b[3]若小于0,对于有效范围内没有数据可以填补两者。(b[2]想变成正数就需要b[2-1-3]减一,但不存在,b[3]同理)
  3. 最后在b[len]后添加一个b[len+1]==0-a[len],用于减去最后一个元素a[len]。

接下来就很简单了,就是拿前面的正数去弥补后面的负数。如果正好为0则说明可以,否则不可以。

其中还要注意边界判断。

  • 考察点:数学,思维,差分

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e5+100;

ll a[maxn],b[maxn];

int main()
{
    ios::sync_with_stdio(false);
    int t,n;
    cin>>t;
    for(int c=1;c<=t;c++){
        cin>>n;
        a[0]=0;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        for(int i=1;i<=n;i++)
            b[i]=a[i]-a[i-1];
        b[n+1]=0-a[n];
        if(b[2]<0||b[3]<0){
            cout<<"Case #"<<c<<": No\n";
            continue;
        }
        ll sum=0;
        for(int i=1;i<=n;i++)
        {
            sum+=b[i];
            int pos=i+3;
            if(pos>n+1)  //边界
                break;
            if(b[pos]<0){
                sum+=b[pos];
                b[pos]=0;
            }
            if(sum<0) break;
        }
        if(sum==0) cout<<"Case #"<<c<<": Yes\n";
        else cout<<"Case #"<<c<<": No\n";
    }
}

F - 非常男女

题意:

中文你我?懂?

思路: 

01可能不是很方便我们统计人数,将代表女生的0换成-1,那么这道题就转换成求一个最长子序列满足序列和为0。

  • 考察点:前缀和,思维

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn=1e5+500;
int a[maxn];
int sum[maxn];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==0) a[i]=-1;
    }
    sum[0]=0;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
    }
    map<int,int> mp;
    int len=-1;
    for(int i=1;i<=n;i++){
        if(sum[i]==0){
            len=max(len,i);
        }
        else{
            if(mp[sum[i]]&&len<i-mp[sum[i]])
                len=i-mp[sum[i]];
            else if(!mp[sum[i]])
                mp[sum[i]]=i;
        }
    }
    cout<<len<<endl;
}

G - 矩形A + B

题意:

 中文你我?懂?

思路:

排列组合问题,共有(n+1)条横着的线和(m+1)条竖着的线,问可以产生多少个矩形?

  • 考察点:数学,思维,排列组合

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int main()
{
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        cout<<((n+1)*n/2)*((m+1)*m/2)<<endl;  //C[2](n+1) * C2(m+1)
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值