刷题集合4

 递增三元组

题目描述
给定三个整数数组
A = [A1, A2, … AN],
B = [B1, B2, … BN],
C = [C1, C2, … CN],
请你统计有多少个三元组(i, j, k) 满足:

1 <= i, j, k <= N
Ai < Bj < Ck
输入
第一行包含一个整数N。
第二行包含N个整数A1, A2, … AN。
第三行包含N个整数B1, B2, … BN。
第四行包含N个整数C1, C2, … CN。
1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000

输出
一个整数表示答案

样例输入
3
1 1 1
2 2 2
3 3 3
样例输出
27

#include<iostream>  //二分
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],b[N],c[N];

int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=0;i<n;i++){
        cin>>b[i];
    }for(int i=0;i<n;i++){
        cin>>c[i];
    }
     ll ans=0;
    sort(a,a+n);
    sort(b,b+n);
    sort(c,c+n);
    for(int i=0;i<n;i++){
        int l=0,r=n-1;
        int mid=0;
        int x1=0;
        while(l<r){  //没有=
            mid=l+r+1>>1;
            if(a[mid]<b[i])l=mid;
            else r=mid-1;
            
        }
      
        if(a[l]>=b[i]){ 
           l=-1;
        }
       x1=l;
        l=0,r=n-1;
     
        int x2=0;
        while(l<r){
            mid=l+r>>1;
            if(c[mid]>b[i])r=mid;
            else l=mid+1;
        }
        
        if(c[l]<=b[i]){
            l=n;
        }
        x2=l;
        ans+=(ll)(x1+1)*(n-x2);   //a中下标加一,b中n-1-下标+1;
        
    }
   
  
      
    cout<<ans;
}

二分简介

  • upper_bound() 找到的是大于目标数的位置
  • lower_bound() 找到的是大于等于目标数的位置

#include<iostream>  
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],b[N],c[N];


int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }for(int i=1;i<=n;i++){
        cin>>c[i];

    }
     ll ans=0;
     sort(a+1,a+n+1);
     sort(c+1,c+n+1);
  for(int i=1;i<=n;i++){
      int key=b[i];
      int pos1=lower_bound(a+1,a+n+1,key)-a-1;//大于等于b[i]的第一个数下标不取,减去1;
      int pos2=upper_bound(c+1,c+n+1,key)-c;//大于b[i]的第一个数下标;
       if(pos1 >= 1 && pos2 <= n)
      ans+=(ll)pos1*(n-pos2+1);//c中少算一个数,加上
  }
      
    cout<<ans;
}

双指针

#include<iostream>  
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],b[N],c[N];


int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
             scanf("%d",&b[i]);
    }for(int i=1;i<=n;i++){
         scanf("%d",&c[i]);

    }
     ll ans=0;
     sort(a+1,a+n+1);
     sort(b+1,b+n+1);//a,b,c数组必须全部排好序,在下面pos1与Pos2的位置可以不用重置为1;减少时间复杂度
     sort(c+1,c+n+1);
      int pos1=1,pos2=1;//在循环外重置
  for(int i=1;i<=n;i++){
      int key=b[i];
       while(pos1<=n&&a[pos1]<key)pos1++;//等效于lower_bound
       while(pos2<=n&&c[pos2]<=key)pos2++;//等效于upper_bound
      
       ans+=(ll)(pos1-1)*(n-pos2+1);
  }
      
    cout<<ans;
}

前缀和思想 

#include<iostream>  //前缀和
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],b[N],c[N];
int s1[N],s2[N];//分别统计a[i]与c[i]出现次数

int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        s1[a[i]]++;
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }for(int i=1;i<=n;i++){
        cin>>c[i];
        s2[c[i]]++;
    }
     ll ans=0;
    for(int i=1;i<=N;i++){                                        
        s1[i]+=s1[i-1];
        s2[i]+=s2[i-1];
    }
   for(int i=1;i<=n;i++){
       ans+=(s1[b[i]-1]*ll(s2[N]-s2[b[i]]));//注意n与N,S2[N]是要取所有c中所有元素数量之和;
   }
  
      
    cout<<ans;
}

 1245. 特别数的和

小明对数位中含有 2、0、1、92、0、1、9 的数字很感兴趣(不包括前导 00),在 11 到 4040 中这样的数包括 1、2、9、101、2、9、10 至 32、3932、39 和 4040,共 2828 个,他们的和是 574574。

请问,在 11 到 nn 中,所有这样的数的和是多少?

输入格式

共一行,包含一个整数 nn。

输出格式

共一行,包含一个整数,表示满足条件的数的和。

数据范围

1≤n≤100001≤n≤10000

输入样例:

40
输出样例:

574

#include<iostream>  //水题
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
bool check(int x){
    int flage=0;
    while(x>0){
        int w=x%10;
        if(w==2||w==1||w==9||w==0){
            return 1;
        }
        x=x/10;
    }
    return 0;
}
int main(){
    int n;
    cin>>n;
    int ans=0;
    for(int i=1;i<=n;i++){
        if(check(i)){
            ans+=i;
        }
    }
    cout<<ans;
    return 0;
}

 1204. 错误票据

题目描述
某涉密单位下发了某种票据,并要在年终全部收回。
每张票据有唯一的ID号。全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。
因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。
你的任务是通过编程,找出断号的ID和重号的ID。
假设断号不可能发生在最大和最小号。
输入
要求程序首先输入一个整数N(N< 100)表示后面数据行数。
接着读入N行数据。
每行数据长度不等,是用空格分开的若干个(不大于100个)正整数(不大于100000),请注意行内和行末可能有多余的空格,你的程序需要能处理这些空格。
每个整数代表一个ID号。
输出
要求程序输出1行,含两个整数m n,用空格分隔。
其中,m表示断号ID,n表示重号ID
样例输入
2
5 6 8 11 9
10 12 9
样例输出
7 9

#include<iostream>  //将数存入数组后,排序,比较后一个数与前一个数的差值
#include<cstring>
#include<cstdio>
#include<sstream>
#include<algorithm>
using namespace std;
const int N=10010;
int a[N];
int main(){
    int n=0,cnt;
    int u,v;
    string line;
    cin>>cnt;
    getline(cin,line);
    while(cnt--){
        getline(cin,line);
        stringstream ssin(line);
        while(ssin>>a[n])n++;
    }
    sort(a,a+n);
    for(int i=1;i<n;i++){
        if(a[i]==a[i-1]){
            u=a[i];
        }
        if(a[i]-a[i-1]>=2){
            v=a[i]-1;
        }
    }
    printf("%d %d",v,u);
    
}

哈希表:

#include<iostream>  //存入哈希表中,从最小的数开始判断缺数,或重数
#include<cstring>
#include<cstdio>
#include<sstream>
#include<algorithm>
using namespace std;
const int N=100001;
int a[N];
bool st[N];
int main(){
    int n=0,cnt;
    int u,v;
    string line;
    cin>>cnt;
    getline(cin,line);
    while(cnt--){
        getline(cin,line);
        stringstream ssin(line);
        while(ssin>>a[n])n++;
    }
    int min1=999999;
    for(int i=0;i<n;i++){
          if(st[a[i]])v=a[i];
        st[a[i]]=true;
          
        min1=min(min1,a[i]);
    }
   
    for(int i=min1;i<min1+n;i++){
        if(!st[i]){
            u=i;
              break;
        }
    }
  
    cout<<u<<" "<<v;
    
}

 哈希优化版本:

#include<iostream>  
#include<cstring>
#include<cstdio>
#include<sstream>
#include<algorithm>
using namespace std;
const int N=100001,INF=0x3f3f3f3f;
int hp[N];
int a[N];
bool st[N];
int main(){
    int n=0,cnt;
    int tp;
    int u,v;
    cin>>cnt;
    int minv=INF,maxv=-INF;
    while(cin>>tp){
       hp[tp]++;
       minv=min(minv,tp);
       maxv=max(maxv,tp);
    }
    for(int i=minv;i<=maxv;i++){
        if(hp[i]==2){
            v=i;
        }
        if(hp[i]==0){
            u=i;
        }
    }
    cout<<u<<' '<<v;
  
    
}

 回文日期;

20202020 年春节期间,有一个特殊的日期引起了大家的注意:20202020 年 22 月 22 日。

因为如果将这个日期按 “yyyymmdd” 的格式写成一个 88 位数是 20200202,恰好是一个回文数。

我们称这样的日期是回文日期。

有人表示 20200202 是“千年一遇” 的特殊日子。

对此小明很不认同,因为不到 22 年之后就是下一个回文日期:20211202 即 20212021 年 1212 月 22 日。

也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。

对此小明也不认同,因为大约 100100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 21212121 年 1212 月 1212 日。

算不上“千年一遇”,顶多算“千年两遇”。

给定一个 88 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。

注意

下一个回文日期和下一个 ABABBABA 型的回文日期可能是同一天。

ABABBABA 型的回文日期,需要满足 A≠B。

输入格式

输入包含一个八位整数 N,表示日期。

输出格式

输出两行,每行 11 个八位数。

第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。

数据范围

对于所有评测用例,10000101≤N≤89991231,保证 N 是一个合法日期的 88 位数表示。

输入样例:
20200202
输出样例:
20211202
21211212

#include<iostream>  
#include<cstring>
#include<cstdio>
#include<algorithm>
int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int a[8];
using namespace std;
bool check(int x){
    int year=x/10000;
    int month=x/100%100;
    int day=x%100;
    if(month>12&&!month)return 0;   //判断月 大于12或为0
    if(year%4==0&&year/100!=0||year%400==0){
        months[2]=29;
    }
 
    if(!day||months[month]<day)return 0;   //判断日   超越相应月份最大天数或为0
     months[2]=28;
    return 1;
}
bool judge(int x){    
   
    int k=0;
    if(!check(x)){
        return 0;
    }
    while(x){
        a[k++]=x%10;
        x/=10;
    }

    for(int i=0,j=7;i<j;i++,j--){//i<j
        if(a[i]!=a[j])return 0;
    }
    return 1;
}
int main(){
    int n;
    cin>>n;
    int i=0,j=0;
    while(1){
        if(j==1&&i==1)break;
        if(judge(++n)){
         
            if(j!=1){
            j++;
            cout<<n<<endl;}
            if(a[0]!=a[1]&&a[0]==a[2]&&a[1]==a[3]){
                i++;
                cout<<n<<endl;
            }
        }
    }
}

 466. 回文日期

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 8 位数字表示一个日期,其中,前 4 位代表年份,接下来 2 位代表月份,最后 2 位代表日期。

显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8 位数字是回文的。

现在,牛牛想知道:在他指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。

一个 8位数字是回文的,当且仅当对于所有的 i(1≤i≤8) 从左向右数的第 i 个数字和第 9−i 个数字(即从右向左数的第 i 个数字)是相同的。

例如:

•对于2016年11月19日,用8位数字20161119表示,它不是回文的。

•对于2010年1月2日,用8位数字20100102表示,它是回文的。

•对于2010年10月2日,用8位数字20101002表示,它不是回文的。

每一年中都有12个月份:

其中,1、3、5、7、8、10、12月每个月有31天;4、6、9、11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

1.这个年份是4的整数倍,但不是100的整数倍;

2.这个年份是400的整数倍。

例如:

•以下几个年份都是闰年:2000、2012、2016。

•以下几个年份是平年:1900、2011、2014。

输入输出格式
输入格式:
输入包括两行,每行包括一个8位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证date_i和都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。

保证date1 —定不晚于date2。

输出格式:
输出一行,包含一个整数,表示在date1和date2之间,有多少个日期是回文的。

输入样例:
20110101
20111231
输出样例:
1

该题与上题不同点在于使用枚举日期,判断是否是回文数的做法,会超时

因此,我们先将所有回文数枚举,判断是否在范围内且是合法日期;

#include<iostream>  
#include<cstring>
#include<cstdio>
#include<algorithm>
int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int a[8];
using namespace std;
bool check(int x){
    int year=x/10000;
    int month=x/100%100;
    int day=x%100;
    if(!month||month>12)return 0;   //判断月
    if(year%4==0&&year/100!=0||year%400==0){
        months[2]=29;
    }
    if(!day||months[month]<day)return 0;   //判断日
    months[2]=28;
    return 1;
}
int  f(int x){
    int a=0;
    while(x){
        a=a*10+x%10;
        x=x/10;
    }
    return a;
}
int main(){
    int start,end,ans=0;
    cin>>start>>end;
    for(int i=1000;i<=9999;i++){
        int x=i*10000+f(i);
        if(x>=start&&x<=end&&check(x)){
            ans++;
        }
    }
 
    cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值