国庆清北刷题冲刺班 Day1 上午

75 篇文章 0 订阅
51 篇文章 0 订阅

位运算1(bit)

Time Limit:1000ms Memory Limit:128MB
题目描述
LYK拥有一个十进制的数N。它赋予了N一个新的意义:将N每一位都拆开来后再加起来就是N所拥有的价值。例如数字123拥有6的价值,数字999拥有27的价值。
假设数字N的价值是K,LYK想找到一个价值是K-1的数字,当然这个答案实在太多了,LYK想使得这个价值为K-1的数字尽可能大。
输入格式(bit.in)
一个数N。
输出格式(bit.out)
一个数表示答案。你需要输出一个非负整数,且这个数不包含前导0。
输入样例1
199
输出样例1
198
输入样例2
1000
输出样例2
0
对于20%的数据n<=10
对于40%的数据n<=100
对于60%的数据n<=1000
对于100%的数据1<=n<=100000。

//只要脑子不是装饰品
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int s[1000005];

int main(int argc,char *argv[]){
    freopen("bit.in","r",stdin);
    freopen("bit.out","w",stdout);
    int n,cnt=0;
    scanf("%d",&n);
    while(n){
        s[++cnt]=n%10;
        n/=10;
    }
    for(int i=1;i<=cnt;++i)
        if(s[i]>=1){ s[i]-=1;break; }
    int i;
    for(i=cnt;i>1&&s[i]==0;--i);
    for(int j=i;j>=1;--j) printf("%d",s[j]);
    fclose(stdin);fclose(stdout);
    return 0;
}

火柴棒 (stick)

Time Limit:1000ms Memory Limit:128MB
题目描述
众所周知的是,火柴棒可以拼成各种各样的数字。具体可以看下图:
通过2根火柴棒可以拼出数字“1”,通过5根火柴棒可以拼出数字“2”,以此类推。
现在LYK拥有k根火柴棒,它想将这k根火柴棒恰好用完,并且想知道能拼出的最小和最大的数分别是多少。
输入格式(stick.in)
一个数k。
输出格式(stick.out)
两个数,表示最小的数和最大的数。注意这两个数字不能有前导0。
输入样例
15
输出样例
108 7111111
数据范围
对于30%的数据k<=10。
对于60%的数据k<=20。
对于100%的数据1

//DP  一下  最大的情况 一定只有一个7和1  或者全是1  
//最小的情况   DP  不难懂
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

#define LL long long
LL dp[105];
int f[15];
int main(int argc,char *argv[]){
    freopen("stick.in","r",stdin);
    freopen("stick.out","w",stdout);
    f[1]=2,f[2]=5,f[3]=5,f[4]=4,f[5]=5;
    f[6]=6,f[7]=3,f[8]=7,f[9]=6,f[0]=6;
    dp[2]=1,dp[3]=7,dp[4]=4,dp[5]=2,dp[6]=6,dp[7]=8;
    int k;
    for(int i=8;i<=100;++i){
        dp[i]=dp[i-f[0]]*10;
        for(int j=0;j<=9;++j)
            if(dp[i-f[j]]) dp[i]=min(dp[i],dp[i-f[j]]*10+j);
    }
    scanf("%d",&k);
    printf("%lld ",dp[k]);
    if(k%2==0){
        for(int i=1;i<=k/2;++i) printf("1");
    }
    else {
        k-=3;k/=2;
        printf("7");
        for(int i=1;i<=k;++i) printf("1");
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

听音乐(music)

Time Limit:1000ms Memory Limit:128MB
题目描述
LYK喜欢听音乐,总共有n首音乐,有m个时刻,每个时刻LYK会听其中一首音乐,第i个时刻会听第ai首音乐。它给自己定了一个规定,就是从听音乐开始,听的每连续n首音乐都是互不相同的。例如当n=3时,从听歌开始,123321就是一个合法的顺序(此时LYK听了两轮歌,分别是123和321,每一轮的歌都是互不相同的),而121323就是一个不合法的顺序(LYK也听了两轮歌,第一轮中121存在听了两次相同的歌)。我们现在只截取其中一个片段,也就是说并不知道LYK之前已经听了什么歌。因此121323也仍然可以是一个合法的顺序,因为LYK之前可能听过3,然后再听121323,此时LYK听了三轮歌,分别是312,132和3。
现在LYK将告诉你这m个时刻它听的是哪首歌。你需要求出LYK在听这m首歌之前可能听过的歌的不同方案总数(我们认为方案不同当且仅当之前听过的歌的数量不同)。LYK向你保证它之前听过的歌的数量是在0~n-1之间的。因此你输出的答案也应当是0~n中的某个整数(答案是0表示LYK记错了,没有一个合法的方案)。
输入格式(music.in)
第一行两个数n,m。
第二行m个数表示ai。
输出格式(music.out)
一个数表示答案。
输入样例1
4 10
3 4 4 1 3 2 1 2 3 4
输出样例1
1
样例解释1:LYK之前一定只听过2首歌(12或者21),这样可以分成3部分分别是34,4132,1234,每一部分都没有出现相同的歌。对于其它情况均不满足条件。
输入样例2
6 6
6 5 4 3 2 1
输出样例2
6
样例解释2:LYK之前听过0~5首歌的任意几首都是有可能满足条件的。
数据范围
对于50%的数据n,m<=1000。
对于100%的数据1<=n,m<=100000,1<=ai<=n。
其中均匀分布着n < m以及n>=m的情况。
提示:
LYK知道这个题目很长,但为了便于理解已经加了很多注释了……建议没看懂的同学们再重新看一遍…

分析:比较有意思的一道题,首先我们最多将序列分成m/n + 2段,那么我们花O(n)的时间枚举最前面一段,再花O(m/n + 2)的时间枚举中间段,如果能够做到O(1)查询,那么复杂度就是O(n + m).。考虑怎么O(1)查询,当然边枚举边处理肯定是不行的,我们需要预处理.每次维护一个长度为n的区间[l,r],每次移动区间:r++,l++,同时v2[a[r]]++,v2[a[l]]–,最后有没有v2等于2的,事实上就是一个动态维护.记录下当前左端点是否可行即可.对于最前面和最后面的区间我们分别记录一个前缀和一个后缀就可以了.最后枚举要分多少段,在维护后缀的时候同时扫一下看看能不能满足要求即可.
如果我们把算法看做是一个又一个操作的和,那么我们看看能优化哪些操作的复杂度,比如本题的操作就有枚举操作和查询操作,枚举是O(n)不能优化了,尽最大的可能去优化查询操作。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100005
int v[MAXN],V1[MAXN],V2[MAXN],a[MAXN],n,m,Ans;

inline bool Judge(int x){
    int i;
    for (i=x-n+1; i>=1; i-=n)
        if(!V2[i]) return false;
    i+=n;
    if(i-1>0 && !V1[i-1]) return false;
    return true;
}

inline void read(int &x){
    x=0; int f=1; char c=getchar();
    while(c>'9'||c<'0'){ if(c=='-')f=-1; c=getchar(); }
    while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}

int main(int argc,char *argv[]){
    freopen("music.in","r",stdin);
    freopen("music.out","w",stdout);
    read(n),read(m);
    for(int i=1;i<=m;++i) read(a[i]);
    int Sum=0;
    for(int i=1;i<=min(n,m);++i){
        ++v[a[i]];
        if(v[a[i]]==2) Sum++;
        if(!Sum) V1[i]=1;
        else V1[i]=0;
    }
    if(!Sum) V2[1]=1;
    else V2[1]=0;
    for (int i=n+1; i<=m; ++i){
        ++v[a[i]];
        if(v[a[i]]==2) ++Sum;
        --v[a[i-n]];
        if(v[a[i-n]]==1) --Sum;
        if(!Sum) V2[i-n+1]=1;
        else V2[i-n+1]=0;
    }
    memset(v,0,sizeof v );
    Sum=0;
    int j;
    for(j=m; j>=1; --j){
        ++v[a[j]];
        if(v[a[j]]>=2) break;
    }
    for(int i=m; i>max(0,m-n); --i)
        if(j<=i && Judge(i)) ++Ans;
    if(Ans) printf("%d\n",(n>=m && Ans==m ? n : Ans));
    else puts("0");
    fclose(stdin);fclose(stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值