【笔试】美团2024年春招第二场笔试(技术)
文章目录
-
-
- [T1 模拟](#T1 模拟)
- [T2 模拟](#T2 模拟)
- [T3 模拟,快速幂/打表](#T3 模拟,快速幂/打表)
- [T4 众数、前缀和、树状数组](#T4 众数、前缀和、树状数组)
- [T5 逆序对,树状数组](#T5 逆序对,树状数组)
-
T1 模拟
题目:数组求和,判断是否要减一个数
思路:模拟即可
cpp
//T1
//AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int n; cin>>n;
LL sum = 0;
for(int i = 1; i <= n; i++){
LL x; cin>>x;
sum += x;
}
LL a, b; cin>>a>>b;
sum = sum-a-b;
cout<<sum<<"\n";
}
T2 模拟
题目:
- 1、所有字母都是小写。例如:good
2、所有字母都是大写。例如:APP
3、第一个字母大写,后面所有字母都是小写。例如:Alice - 给个字符串最少几次能合法
思路:
- 最多四种情况判断一下即可
cpp
//T2
//AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
string s; cin>>s;
int sm = 0;
for(int i = 0; i< s.size(); i++){
if(islower(s[i])){
sm++;
}
}
int bg = s.size()-sm;
int ans = min(bg, sm);
if(isupper(s[0])){
ans = min(ans, bg-1);
}
cout<<ans<<"\n";
}
T3 模拟,快速幂/打表
- 题目:数组,每次操作将除了第 x 个元素的其余元素翻倍,操作q次,求最后的数组和。
- 思路:先假设每个数每次都翻倍,维护每个数没有翻倍的次数,最后除回去就行。
cpp
//T3
//AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
const LL mod = 1e9+7;
LL a[maxn], v[maxn];
LL pows[maxn];
int main() {
pows[0] = 1;
for(int i = 1; i < maxn; i++){
pows[i] = pows[i-1]*2%mod;
}
int n, q; cin>>n>>q;
for(int i = 1; i <= n; i++){
cin>>a[i];
}
LL fb = q;
while(q--){
int xi; cin>>xi;
v[xi]++;
}
LL ans = 0;
for(int i = 1; i <= n; i++){
ans = (ans + (pows[fb-v[i]]*a[i]%mod))%mod;
}
cout<<ans<<"\n";
}
T4 众数、前缀和、树状数组
题目:求数组的所有子数组的众数之和,众数有多个时取小的那个。
输入
3
2 1 2
输出
9
思路1:
- 取值只有1和2,肯定要用起来。考虑对答案的贡献,默认所有区间+1,因为至少1嘛。然后最多也就是2,什么情况下会是2呢,对于区间和>区间长度*1.5的,也就是2的个数超过区间一半的时候。
- 暴力所有区间n^2,然后前缀和O1区间和判断累加,到这能拿70%(其实也就是找2是众数的区间有多少个的情况)
cpp
//T4-70%
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+10;
LL a[maxn], s[maxn];
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
LL n; cin>>n;
LL ans = 0;
for(LL i = 1; i <= n; i++){
cin>>a[i];
// a[i]--;
// ans += a[i];
s[i] = s[i-1]+a[i];
}
// for(LL l = 1; l <= n; l++){
// for(LL r = l; r <= n; r++){
// LL sum = s[r]-s[l-1];
// LL c2 = sum-(r-l+1);
// LL c1 = (r-l+1)-c2;
// // for(int k = l; k <= r; k++){
// // if(a[k]==1)c1++;
// // else c2++;
// // }
// if(c1>=c2)ans += 1;
// else ans += 2;
// }
// }
// 找出区间和比1.5倍区间长度要长的,+2(或者说加+1,然后加全部区间个数)
ans = (1+n)*n/2;
for(int len = 1; len <= n; len++){
for(int i = 1; i+len<=n; i++){
if((s[i+len-1]-s[i-1]) > len+len/2+len%2)ans++;
}
}
cout<<ans<<"\n";
// LL nn = (1+n)*n/2;
// LL s1 = 0, s2 = 0;
// for(int i = 1; i <= n; i++){
// LL x; cin>>x;
// if(x==1)s1++;
// else s2++;
// }
// cout<<nn*s2/s1<<"\n";
}
思路2:
- 考虑区间枚举怎么优化,考虑区间总个数一样,换成求1是众数的的区间有多少个。
- 把1和2换成-1和1。数组的众数是 1 等价于数组的和不小于 0,此时只需要找出有多少个子数组的和不小于 0 (剩下的就是众数2)。 这里还是前缀和, 前面有几个前缀和 ≤ 当前的前缀和。
- 这里有点类似于求逆序对时候的做法,用下标为前缀和,权值为 1 的树状数组 统计某个取值范围内,有多少个前缀和。
cpp
//T4-AC
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 2e5+10;
LL n, v[maxn*2+10];
void add(LL x, LL y){
for(LL i = x; i <= 2*maxn; i += i&(-i)){ // n-> 2maxn
v[i] += y;
}
}
LL query(LL x){
LL res = 0;
for(LL i = x; i > 0; i -= i&(-i)){
res += v[i];
}
return res;
}
int main() {
cin>>n;
LL t = 0, res = 0;
add(maxn, 1LL); //有负数,需要转换一下
for(LL i = 1; i <= n; i++){
LL x; cin>>x;
if(x==1LL)t++; else t--;
res += query(t+maxn);
// cout<<res<<"\n";
add(t+maxn, 1LL);
}
LL cnt = n*(n+1)/2;
// cout<<res<<"\n";
cout<<res+2*(cnt-res)<<"\n";
}
T5 逆序对,树状数组
题目:给你一个排列,定义f(i)为a[i]取反后形成的数组的逆序对数量,求f(1)到f(n)的值。
思路:不难想到,先来个原先的逆序对。 然后扫一遍,过程中维护一下左右比当前数的大小关系,推导一下公式算一下即可。
cpp
//T5
//AC
// = 逆序对-l大于ai-r小于ai+l全部
// = 逆序对+l小于ai-r小于ai
// 排列:ai-1 = l小于ai+r小于ai,维护左边比ai小的数的个数
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 200010;
struct node{ LL v, p; }a[maxn];
bool cmp(node x, node y){return x.v<y.v; }
LL n;
LL v[maxn], res[maxn];
void add(LL x, LL y){
for(LL i = x; i <= n; i += i&(-i)){
v[i] += y;
}
}
LL query(LL x){
LL res = 0;
for(LL i = x; i > 0; i -= i&(-i)){
res += v[i];
}
return res;
}
int main() {
cin>>n;
for(LL i = 1; i <= n; i++){
cin>>a[i].v; a[i].p = i;
}
sort(a+1,a+n+1,cmp);
LL ans = 0;
for(LL i = n; i>= 1; i--){
// cout<<a[i].v<<"\n";
add(a[i].p,1);
ans += query(a[i].p-1);
LL lmax = query(a[i].p-1);
LL lmin = a[i].p-1-lmax;
LL rmin = a[i].v-1-lmin;
res[a[i].p] = lmin-rmin;
}
// cout<<ans<<"\n";
for(LL i = 1; i <= n; i++){
cout<<ans+res[i]<<" ";
}
}
// 64 位输出请用 prLLf("%lld")