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 (a1−x)2+(a2−x)2+(a3−x)2+....+(an−1−x)2+(an−x)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(n∗max(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 1≤i,j,k≤m 且 ( 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;
}