程序设计思维 CSP-M2

A-HRZ的序列

题目

相较于咕咕东,瑞神是个起早贪黑的好孩子,今天早上瑞神起得很早,刷B站时看到了一个序列 ,他对这个序列产生了浓厚的兴趣,他好奇是否存在一个数k,使得一些数加上k,一些数减去k,一些数不变,使得整个序列中所有的数相等,其中对于序列中的每个位置上的数字,至多只能执行一次加运算或减运算或是对该位置不进行任何操作。由于瑞神只会刷B站,所以他把这个问题交给了你!

Input

输入第一行是一个正整数t表示数据组数。 接下来对于每组数据,输入的第一个正整数n表示序列a的长度,随后一行有n个整数,表示序列a。

Output

输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)

Sample Input

2
5
1 2 3 4 5
5
1 2 3 4 5

Sample Ouput

NO
NO

思路

满足题意的序列有三种情况:

  • 全部为a;
  • 一部分为a,一部分为2a;
  • 一部分为a,一部分为2a,一部分为3a。

使序列a有序后,作差,令b[i]=a[i+1]-a[i]。那么,由符合要求的序列a得出的序列b应满足:

  • 全部为零;
  • 有一个不为零,其余均为零;
  • 有两个不为零,其余均为零,且两个不为零的数相等

遍历数组b[],判断是否满足要求。

代码

#include <cstdio>
#include <algorithm>
using namespace std;
long long a[10005];
long long b[10005];

int main(){
    int t;
    scanf("%d",&t);
    for(int i=0;i<t;i++){
        int n;
        scanf("%d",&n);
        for(int j=0;j<n;j++)
            scanf("%lld",&a[j]);
        sort(a,a+n);
        for(int j=1;j<n;j++)
            b[j-1]=a[j]-a[j-1];
        long long k=0;
        bool firstK=false,flag=true;
        int count=0;
        for(int j=0;j<n-1;j++){
            if(b[j]!=0){
                if(!firstK){
                    k=b[j];
                    firstK=true;
                    count++;
                }
                else{
                    if(k!=b[j]){
                        flag=false;
                        break;
                    }
                    count++;
                }
            }
        }
        if(flag&&count<=2)
            printf("YES\n");
        else
            printf("NO\n");
    }
}

总结

第一遍的代码判断yes的条件写成了flag&&count==2,后来又多编了几组数据发现有问题,于是改成了flag&&count<=2,太刺激了。


B-HRZ 学英语

题目

瑞神今年大三了,他在寒假学会了英文的26个字母,所以他很兴奋!于是他让他的朋友TT考考他,TT想到了一个考瑞神的好问题:给定一个字符串,从里面寻找连续的26个大写字母并输出!但是转念一想, 这样太便宜瑞神了,所以他加大了难度:现在给定一个字符串,字符串中包括26个大写字母和特殊字 符’?’,特殊字符’?'可以代表任何一个大写字母。现在TT问你是否存在一个位置连续的且由26个大写字 母组成的子串,在这个子串中每个字母出现且仅出现一次,如果存在,请输出从左侧算起的第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那个解!如果 不存在,输出-1! 这下HRZ蒙圈了,他刚学会26个字母,这对他来说太难了,所以他来求助你,请你帮 他解决这个问题,报酬是可以帮你打守望先锋。 说明:字典序先按照第一个字母,以 A、B、C……Z 的顺序排列;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母。如果比到最后两个单词不一样长(比如,SIGH 和 SIGHT),那么把短者排在前。
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ACBDEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法,都可以构成26个字母,但是我们要求字典序最小,只能取前者。 注意,题目要求的是第一个出现的,字典序最小的!

Input

输入只有一行,一个符合题目描述的字符串。

Ouput

输出只有一行,如果存在这样的子串,请输出,否则输出-1

Sample Input

ABC??FGHIJK???OPQR?TUVWXY?

Sample Ouput

ABCDEFGHIJKLMNOPQRSTUVWXYZ	

思路

类似于滑动窗口。
首先判断读入的字符串长度是否大于等于26,若小于,则一定无法符合要求,输出-1并返回。
接下来,使用sum[]数组记录窗口内每个字母出现的次数以及’?‘出现的次数。将滑动窗口初始化为字符串的前26个字符
然后进入滑动窗口的部分。当滑动窗口未到达字符串尾时,循环。判断是否满足条件:每个字母出现的次数小于等于1次。若符合要求,结束滑动窗口的循环;若不符合要求,窗口右移一个字符。
判断是因为到达字符串尾结束循环还是因为满足要求结束循环。若前者,输出-1并返回;若后者,进入输出部分。
遍历26个字母,若在窗口中出现的次数为0,则当前字母进入队列。
遍历窗口中的字符,若为’?’,则输出队首字母并弹出;否则输出当前字符

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <string>
#include <cstring>
using namespace std;
int sum[30];
string s;
queue<char>q,tmp;
bool judge(){
    for(int i=0;i<26;i++){
        if(sum[i]>1)
            return false;
    }
    return true;
}

int main(){
    cin>>s;
    memset(sum, 0, sizeof(sum));
    if(s.length()<26){
        printf("-1");
        return 0;
    }
    for(int i=0;i<26;i++){
        if(s[i]=='?')
            sum[26]++;
        else{
            sum[s[i]-'A']++;
        }
        q.push(s[i]);
    }
    int l=0,r=25;
    while(r<s.length()&&l<=r){
        if(judge()){
            break;
        }
        l++;r++;
        char c=q.front();q.pop();
        if(c=='?')
            sum[26]--;
        else
            sum[c-'A']--;
        q.push(s[r]);
        sum[s[r]-'A']++;
    }
    if(!judge())
        printf("-1");
    else{
        for(int i=0;i<26;i++){
            if(sum[i]==0)
                tmp.push((char)('A'+i));
        }
        for(int i=0;i<26;i++){
            char c=q.front();q.pop();
            if(c=='?'){
                printf("%c",tmp.front());
                tmp.pop();
            }
            else
                printf("%c",c);
        }
    }
    return 0;
} 

总结

真是太愚蠢了。模拟的时候竟然把judge()函数的i<26写成了i<25,🧠进水了吧,然后就水到了90分……良心不安……


C-咕咕东的奇妙序列

题目

咕咕东正在上可怕的复变函数,但对于稳拿A Plus的 咕咕东 来说,她早已不再听课,此时她在睡梦中突然想到了一个奇怪的无限序列:112123123412345 …这个序列由连续正整数组成的若干部分构成,其 中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字。所以,这个序列的前56项会是 11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是 5,第38项是2,第56项是0。咕咕东现在想知道第 k 项数字是多少!但是她睡醒之后发现老师讲的东西已经听不懂了,因此她把这个任务交给了你。

Input

输入由多行组成。 第一行一个整数q表示有q组询问(1<=q<=500)
接下来第i+1行表示第i个输入ki,表示询问第ki项数字。 1<=ki<=10^18

Ouput

输出包含q行
第i行输出对询问ki的输出结果。

Sample Input

5
1
3
20
38
56

Sample Output

1
2
5
2
0

思路

第1大组(100~101-1)小组间公差为1
1————第1小组(1位)
12————第2小组(2位)
123————第3小组(3位)
······
123456789————第9小组(9位)
第2大组(101~102-1)小组间公差为2
12345678910————第1小组(11位)
1234567891011————第2小组(13位)
123456789101112————第3小组(15位)
······
12345678910···99————第90小组(189位)
······
第i大组(10i-1~10i-1)小组间公差为i
······
用ssum[x]记录x第一次出现(即以x为结尾的小组第一次出现)时的位数;用sum[x]记录1~x的位数。
则ssum可以用等差数列求和公式求得,sum可以用等差数列通项公式求得,其中通项为每一小组的位数。
求第k位的数字。
使用二分法,求ssum[x]<k的最大x。求出x后,意味着所求位数必在紧跟其后的小组中,更新k=k-ssum[x]
使用二分法,求sum[x]<k的最大x。求出x后,意味着所求位数必为数字x+1的某一位,更新k=k-sum[x]
所求即为x+1的第k位。
使用取模及取余,得到数字x+1的每一位,进而得到数字x+1的第k位。

代码

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

void cal(long long x,long long& sum,long long& ssum){
    long long p=1,d=0;
    while(true){
        p*=10;d++;
        if(x>p-1){
            long long n=p-p/10;//sum加几个公差
            ssum+=(sum+d)*n+n*(n-1)/2*d;
            sum+=n*d;
        }
        else{
            long long n=x-p/10+1;
            ssum+=(sum+d)*n+n*(n-1)/2*d;
            sum+=n*d;
            break;
        }
    }
}

int main(){
    int q;
    scanf("%d",&q);
    while(q--){
        long long k,ans=0;
        scanf("%lld",&k);
        long long l=0,r=1e9,sum=0,ssum=0;
        while(l<=r){
            long long mid=(l+r)/2;
            sum=0;ssum=0;
            cal(mid, sum, ssum);
            if(ssum<k){
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        sum=0;ssum=0;
        cal(ans, sum, ssum);
        k-=ssum;
        l=0;r=ans+1;
        while(l<=r){
            long long mid=(l+r)/2;
            sum=0;ssum=0;
            cal(mid, sum, ssum);
            if(sum<k){
                ans=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        sum=0;ssum=0;
        cal(ans, sum, ssum);
        k-=sum;
        ans++;
        vector<int> v;
        v.push_back((int)ans%10);
        while(ans/10>0){
            ans/=10;
            v.push_back((int)ans%10);
        }
        printf("%d\n",v[v.size()-k]);
    }
    return 0;
}

总结

模拟的时候以为自己找到了前55位的规律,但是并没有。于是连60分都没有。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值