HZU蓝桥杯校内第二次选拔赛题解

HZU蓝桥杯校内第二次选拔赛题解

T1

  • 题意: 从 n n n个数中取最大的 k k k个数。
    • 对于 80 80 80%的数据,直接 s o r t sort sort取最大的 k k k个数,时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n)),或者使用优先队列维护 k k k个最大值时间复杂度: O ( n l o g ( k ) ) O(nlog(k)) O(nlog(k))
    • 对于 100 100 100%的数据,根据快速排序分治思路,选取一个数作为基准 k e y key key,如果超过 k k k个数比 k e y key key大,就往大于 k k k的一边递归,如果没有超过 k k k个数比 k e y key key大,就往小一边递归,而且比 k e y key key大的数都在里面。所以我们可以在期望时间复杂度: O ( n ) O(n) O(n)
  • 代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=5e6+5;
const int maxm=1e6;
ll sum;
int a[maxn];
ll q_sort(int l, int r, int k) {
    if (l > r) {  return 0; }
    int p = rand() % (r - l + 1) + l; 
    int x = a[p];
    swap(a[r], a[p]);
    int i = l, j = r;
    while(i < j) {
        while(i < j && a[i] < x) i++;   
        if(i < j) { swap(a[i],a[j]); j--; }
        while(i < j && a[j] > x) j--;   
        if(i < j) { swap(a[i],a[j]); i++; }
    }
    swap(a[i], x);
    ll sum = 0;
    for (int j = i; j <= r; j++) sum += a[j];
    if (r - i >= k) {
        sum = q_sort(i + 1, r, k);
    } else if (k > r - i + 1) {
        sum += q_sort(l, i - 1, k - (r - i + 1));
    }
    return sum;
}
int main() {
    int n, k;
    srand(time(0));
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    printf("%lld\n", q_sort(1, n, k));
    return 0;
}

T2

  • 题意:寻找一个整数 x x x使得公式 : : : ( a 1 − x ) 2 + ( a 2 − x ) 2 + ( a 3 − x ) 2 + . . . . + ( a n − 1 − x ) 2 + ( a n − x ) 2 (a_1-x)^2+(a_2-x)^2+(a_3-x)^2+....+(a_{n-1}-x)^2+(a_n-x)^2 (a1x)2+(a2x)2+(a3x)2+....+(an1x)2+(anx)2最小。

    • 对于 70 70 70%的数据,直接暴力枚举 x x x寻找最小值即可,时间复杂度: O ( n 2 ) O(n^2) O(n2)

    • 对于 100 100 100%的数据,其实这个公式就是数学上的方差公式,所以 x x x就是平均数,因为 x x x必须是整数,所以处理 x x x是大于平均数时最小,还是小于平均数时最小,时间复杂度: O ( n ) O(n) O(n)

  • 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
ll a[maxn];
int main() {
    int T, n;
    ll p = 0, x = 0, t;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        p += a[i]; x += p / n; p %= n;
    }   
    if (2 * p <= n) { printf("%lld\n", x);  }
    else { printf("%lld\n", x + 1); }
    return 0;
}

T3

  • 题意:求满足题意的三元组 ( i , j , k ) (i,j,k) (i,j,k)的个数。
    • 对于 70 70 70%的数据,直接暴力三重循环判断即可,时间复杂度: O ( n 3 ) O(n^3) O(n3)
    • 对于 100 100 100%的数据,我们可以枚举 j j j,然后对 i , k i,k i,k维护一个树状数组即可(记得对 a i a_i ai进行离散化),时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
  • 代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
ll c[maxn], L[maxn], R[maxn];
int n, mx, a[maxn], Hash[maxn], len;
int lowbit(int x) { return x & (-x); }
void add(int x, ll v) {
    for (int i = x; i <= mx; i += lowbit(i)) c[i] += v;
}
ll sum(int x) {
    ll ans = 0;
    for (int i = x; i; i -= lowbit(i)) ans += c[i];
    return ans;
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        Hash[++len] = a[i];
    }
    sort (Hash + 1, Hash + len + 1);
    len = unique(Hash + 1, Hash + len + 1) - Hash - 1;
    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(Hash + 1, Hash + len + 1, a[i]) - Hash;
        R[a[i]]++;
        mx = max(mx, a[i]);
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += sum(a[i] - 1);
        add(a[i], -1 * L[a[i]] * R[a[i]]);
        L[a[i]]++; R[a[i]]--;
        add(a[i], L[a[i]] * R[a[i]]);
    }
    printf("%lld\n", ans);
    return 0;
}

T4

  • 题意:给出 n , a , b n,a,b n,a,b。求出第 n n n个数字能整除 a a a或者整除 b b b,但是不能同时整除 a a a b b b

    • 对于 70 70 70%的数据,暴力枚举每一个数字,进行判断是否满足条件。时间复杂度: O ( m a x ( a , b ) ∗ n ) O(max(a,b)*n) O(max(a,b)n)
    • 对于 100 100 100%的数据,很明显当数字越来越大是,满足条件的数字也会越来越多,满足单调性,所以我们可以进行二分答案 x x x,判断小于 x x x的满足添加的数有多少个即可。时间复杂度: O ( l o g ( n ∗ m a x ( a , b ) ) ) O(log(n*max(a,b))) O(log(nmax(a,b)))
    • 注意特判 a = b a=b a=b时为 − 1 -1 1即可。
  • 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {
    return b?gcd(b,a%b):a;
}
int main() {
    int T;
    ll n, a, b; cin >> n >> a >> b;
    ll d = a * b / gcd(a, b);
    ll l = 1, r = 1e18;
    ll ans = 0;
    if (a == b) {
        printf("-1\n"); return 0;
    }
    while (l <= r) {
        ll mid = (l + r) / 2;
        if (mid/a+mid/b-mid/d*2>=n) {
            ans = mid;
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    cout << ans << endl;
    return 0;
}

T5

  • 题意: 给一长度为 n n n的木棍,要求将其切割成若干长度为 a , b , c , d a,b,c,d a,b,c,d的小木棍,且小木棍数量尽可能多。

    • 对于 70 70 70%的数据,暴力 d f s dfs dfs搜索即可。
    • 对于 100 100 100%的数据,在暴力 d f s dfs dfs搜索的过程中加入记忆化数组即可。
  • 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 1e9 + 7;
int a[maxn], dp[maxn];
int dfs(int x) {
    if (x<0) return -inf;
    if (!x) return 0;
    if (dp[x]) return dp[x];
    dp[x]=-inf;
    for (int i=1;i<=4;i++) dp[x]=max(dp[x], dfs(x-a[i])+1);
    return dp[x];
}
int main() {
    int n; cin >> n;
    for (int i=1;i<=4;i++) cin >> a[i];
    cout << max(dfs(n), -1) << endl;
    return 0;
}

T6

  • 题意: 满足 1 ≤ i , j , k ≤ m 1≤i,j,k≤m 1i,j,km ( i 2 + j 2 + k 2 ) (i^2+j^2+k^2) (i2+j2+k2) % n n n = 0 0 0 ( i , j , k ) (i, j,k) (i,j,k)的个数。

    • 对于 50 50 50%的数据,直接暴力枚举 i , j , k i,j,k i,j,k即可。时间复杂度: O ( m 3 ) O(m^3) O(m3)
    • 对于 70 70 70%的数据,首先我们使用 O ( n 2 ) O(n^2) O(n2)的时间预处理出 ( i 2 + j 2 ) (i^2+j^2) (i2+j2)% n n n的所有余数个数,然后在 O ( n ) O(n) O(n)的时间处理出答案,时间复杂度: O ( n 2 ) O(n^2) O(n2)
    • 对于 100 100 100%的数据,如果我们能快速预处理出 ( i 2 + j 2 ) (i^2+j^2) (i2+j2)% n n n的所有余数个数,我们就可以在 O ( n ) O(n) O(n)的时间处理出答案,所以如何快速处理出所有余数的个数?处理所有余数时,我们发现可以使用FFT算法 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))快处理出所有余数的个数,处理出来之后我们就可以 O ( n ) O(n) O(n)时间内得到答案。时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
  • 代码:

#include<bits/stdc++.h>
#define N 2621450
#define pi acos(-1)
typedef long long ll;
using namespace std;
typedef complex<double> E;
int n,m,l,r[N];
E a[N], b[N];
ll c[N];
void FFT(E *a,int f){
    for(int i=0;i<n;i++)if(i<r[i])swap(a[i],a[r[i]]);
    for(int i=1;i<n;i<<=1){
        E wn(cos(pi/i),f*sin(pi/i));
        for(int p=i<<1,j=0;j<n;j+=p){
            E w(1,0);
            for(int k=0;k<i;k++,w*=wn){
                E x=a[j+k],y=w*a[j+k+i];
                a[j+k]=x+y;a[j+k+i]=x-y;
            }
        }
    }
}
ll dp[N], sum[N];
int main(){
    int q, p;
    srand(time(0));
    cin >> q >> p;
    for (int i = 0; i < q; i++) c[i] = p/q;
    for (int i = 1; i <= p%q; i++) c[i]++;
    for (int i = 0; i < q; i++) a[1LL*i*i%q]+=c[i];
    for (int i = 0; i < q; i++) b[i] = a[i];
    for (int i = 0; i < q; i++) sum[1LL*i*i%q] += c[i];
    n=q; m=q;
    m+=n;for(n=1;n<=m;n<<=1)l++;
    for(int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    FFT(a,1);FFT(b,1);
    for(int i=0;i<=n;i++)a[i]=a[i]*b[i];
    FFT(a,-1);
    for(int i=0;i<=m;i++) dp[i%q] += (ll)(a[i].real()/n+0.5);
    ll ans = dp[0] * sum[0];
    for (int i = 0; i < q; i++) ans += dp[i]*sum[q-i];
    cout << ans << endl;
    return 0;
}

T7

  • 题意:输出话唠的名字 ? ? ??

    • 签到题,直接输出 10 10 10个人名字中任意一个即可 ? ? ? ? ? ? ? ? ???????? 因为每个人都是话唠。
  • 代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
    cout << "zhj"<<endl;
    return 0;
}

T8

  • 题意: 给你一个字符串 S S S(只含 W W W J J J字符),判断 S S S中是否有 W J WJ WJ J W JW JW这两个子串。签到题 ? ? ? ? ? ? ?????? 暴力 n 2 n^2 n2过百万
    • 因为该字符串只含 W W W J J J所我们可以暴力枚举子串 W J WJ WJ,所以最多枚举出现两次子串 W J WJ WJ。时间复杂度: O ( n ) O(n) O(n)
  • 代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+5;
const int maxm=1e6;
char str[maxn];
int main() {
    cin >> str + 1;
    int n = strlen(str + 1);
    for (int i = 1; i <= n; i++) {
        if (str[i] == 'W' && str[i + 1] == 'J') {
            for (int j = 1; j < i; j++) {
                if (str[j] == 'W' && str[j - 1] == 'J') {
                    cout << "YES" << endl; return 0;
                }
            }   
            for (int j = i + 2; j <= n; j++) {
                if (str[j] == 'J' && str[j + 1] == 'W') {
                    cout << "YES" << endl; return 0;
                }
            }
        }
    }
    cout << "NO" << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值