HZU蓝桥杯校内第二次选拔赛题解
T1
- 题意: 从
个数中取最大的
个数。
- 对于
%的数据,直接
取最大的
个数,时间复杂度:
,或者使用优先队列维护
个最大值时间复杂度:
。
- 对于
%的数据,根据快速排序分治思路,选取一个数作为基准
,如果超过
个数比
大,就往大于
的一边递归,如果没有超过
个数比
大,就往小一边递归,而且比
大的数都在里面。所以我们可以在期望时间复杂度:
。
- 对于
- 代码:
#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
题意:寻找一个整数
使得公式
最小。
对于
%的数据,直接暴力枚举
寻找最小值即可,时间复杂度:
。
对于
%的数据,其实这个公式就是数学上的方差公式,所以
就是平均数,因为
必须是整数,所以处理
是大于平均数时最小,还是小于平均数时最小,时间复杂度:
。
代码:
#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
- 题意:求满足题意的三元组
的个数。
- 对于
%的数据,直接暴力三重循环判断即可,时间复杂度:
。
- 对于
%的数据,我们可以枚举
,然后对
维护一个树状数组即可(记得对
进行离散化),时间复杂度:
。
- 对于
- 代码:
#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
题意:给出
。求出第
个数字能整除
或者整除
,但是不能同时整除
和
。
- 对于
%的数据,暴力枚举每一个数字,进行判断是否满足条件。时间复杂度:
。
- 对于
%的数据,很明显当数字越来越大是,满足条件的数字也会越来越多,满足单调性,所以我们可以进行二分答案
,判断小于
的满足添加的数有多少个即可。时间复杂度:
。
- 注意特判
时为
即可。
- 对于
代码:
#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
题意: 给一长度为
的木棍,要求将其切割成若干长度为
的小木棍,且小木棍数量尽可能多。
- 对于
%的数据,暴力
搜索即可。
- 对于
%的数据,在暴力
搜索的过程中加入记忆化数组即可。
- 对于
代码:
#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
题意: 满足
且
%
=
的
的个数。
- 对于
%的数据,直接暴力枚举
即可。时间复杂度:
- 对于
%的数据,首先我们使用
的时间预处理出
%
的所有余数个数,然后在
的时间处理出答案,时间复杂度:
- 对于
%的数据,如果我们能快速预处理出
%
的所有余数个数,我们就可以在
的时间处理出答案,所以如何快速处理出所有余数的个数?处理所有余数时,我们发现可以使用FFT算法
快处理出所有余数的个数,处理出来之后我们就可以
时间内得到答案。时间复杂度:
- 对于
代码:
#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
- 题意:输出话唠的名字
- 签到题,直接输出
个人名字中任意一个即可
因为每个人都是话唠。
- 签到题,直接输出
- 代码:
#include <bits/stdc++.h> using namespace std; int main() { cout << "zhj"<<endl; return 0; }
T8
- 题意: 给你一个字符串
(只含
或
字符),判断
中是否有
和
这两个子串。签到题
暴力
过百万
- 因为该字符串只含
或
所我们可以暴力枚举子串
,所以最多枚举出现两次子串
。时间复杂度:
- 因为该字符串只含
- 代码:
#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; }