蓝桥杯 整数小拼接

题意

给定数组 , 求其中两两组合产生数字$ \le k$的个数

思路

先排序 , 再双指针 , \(i\)指向左边 , \(j\)指向右边

\((i,j)\)配对符合时 , \(ans\) 加上 \(j-i\) , 如果此时\(check(i,i)\)满足 , 说明被算了两次 , 那么\(ans--\) , 然后\(i\)右移

\((i,j)\)配对不符合时 , \(j\)移动直到为\(j=0\)或者 \(check(i,j)\)满足 , 然后同上

几个疑问:

Q : 排序怎么可以不用字典序?

首先 , 只有当位数不同时才可能出现字典序和数值序不同的情况 , 比如 \(9\)\(90\) , 且数值型排序结果必然是低位数在前 , 高位数在后

而在这种情况下 , 对一个确定的 \(a_j\) , 如果低位数的数拼凑\(a_j\)已经超出了预定值 , 那么高位数的拼凑后必然也超出预定值

对一个确定的 \(a_i\) , 从高位数的\(a_j\)向低位数的\(a_j\)测试是满足单调的

因此 , 该题可以用字典序 , 也可以数值排序

Q : 为什么终止条件为\(i \le n\) 而不是 \(i \le j\)?

\(i\)达到某个临界值时 , \(j\)会迅速下降 , 此时\(j\)可能小于\(i\) , 但是仍然应该计数

而当\(i\)跨过临界值时 , \(j\)不一定已经下降到\(0\)(因为位数可能有很多)

只有当\(j\)\(0\)时才是真的无数可走 , 所以可以把循环条件改为\(i<=n && j\)

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {

    int res= 0,f =1;
    char ch = getchar();
    while (ch<'0' || ch > '9') {
        if (ch=='-')f=-1;
        ch = getchar();
    }
    while (ch>='0' && ch <='9') {
        res = res * 10 + ch-'0';
        ch = getchar();
    }
    return res * f;
}
const int N = 1e5+10;
int a[N];
int k;
bool cmp(const string& a,const string& b) {
    string ab = a + b;
    string ba = b + a;
    return ab <ba;
}
bool check(int x,int y) {
    return stoll((to_string(a[x]) + to_string(a[y]))) <= k;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n = read();
    k = read();
    for (int i =1; i<= n; i++) {
        a[i] =read();
    }
    sort(a+1,a+1+n);
    int ans = 0;
    for (int i =1,j=n; i<=n; i++) {
        while (j>0&&!check(i,j))j--;
        ans += j;
        if (j>=i)
            ans--;
    }
    cout<<ans<<"\n";
    return 0;
}
posted @ 2025-04-14 22:09  Guaninf  阅读(13)  评论(0)    收藏  举报