1.小笨的数字权值
以下是针对该问题的C++和C语言解决方案。解决方案的核心思路是预处理出每个数字的最大权值和,然后对每个查询直接输出结果。
问题分析
- 权值定义:一个数字的权值是其正因子个数。
- 目标:将给定的正整数 ( x ) 分解为若干大于1的数字(可以不分解),使得这些数字的乘积等于 ( x ),且它们的权值之和最大化。
- 关键观察:
- 不分解时,权值之和为 ( d(x) )(即 ( x ) 的因子个数)。
- 分解时,将 ( x ) 分解为两个因子 ( a \times b )(( a, b \geq 2 )),则权值之和为 ( f(a) + f(b) ),其中 ( f ) 表示该数字的最大权值和(包括进一步分解的可能)。
- 通过动态规划,从小到大计算每个数字的最大权值和 ( f(x) ),利用 ( f(x) = \max(d(x), \max_{2 \leq a \leq \sqrt{x}} { f(a) + f(b) } )(其中 ( b = x/a ))。
- 预处理:
- 使用筛法预处理每个数字的因子个数 ( d(x) )。
- 初始化 ( f(x) = d(x) )(不分解的情况)。
- 使用动态规划,枚举因子 ( i ) 和 ( j ),更新 ( f[i \times j] = \max(f[i \times j], f[i] + f[j]) )。
C++ 解决方案
#include <iostream>
#include <algorithm>
using namespace std;
const int max_n = 200000;
int d[max_n + 1];
int f[max_n + 1];
int main() {
// 预处理因子个数 d
for (int i = 1; i <= max_n; i++) {
for (int j = i; j <= max_n; j += i) {
d[j]++;
}
}
// 初始化 f(x) = d(x)
for (int i = 1; i <= max_n; i++) {
f[i] = d[i];
}
// 动态规划:更新 f[k] 为分解后的最大权值和
for (int i = 2; i <= max_n; i++) {
for (int j = 2; j <= max_n / i; j++) {
int k = i * j;
if (f[i] + f[j] > f[k]) {
f[k] = f[i] + f[j];
}
}
}
// 处理查询
int T;
scanf("%d", &T);
while (T--) {
int x;
scanf("%d", &x);
printf("%d\n", f[x]);
}
return 0;
}
C 语言解决方案
#include <stdio.h>
#define max_n 200000
int d[max_n + 1];
int f[max_n + 1];
int main() {
// 预处理因子个数 d
for (int i = 1; i <= max_n; i++) {
for (int j = i; j <= max_n; j += i) {
d[j]++;
}
}
// 初始化 f(x) = d(x)
for (int i = 1; i <= max_n; i++) {
f[i] = d[i];
}
// 动态规划:更新 f[k] 为分解后的最大权值和
for (int i = 2; i <= max_n; i++) {
for (int j = 2; j <= max_n / i; j++) {
int k = i * j;
if (f[i] + f[j] > f[k]) {
f[k] = f[i] + f[j];
}
}
}
// 处理查询
int T;
scanf("%d", &T);
while (T--) {
int x;
scanf("%d", &x);
printf("%d\n", f[x]);
}
return 0;
}
代码说明
- 预处理因子个数:
- 使用筛法,对于每个 ( i ),将其倍数 ( j ) 的因子个数 ( d[j] ) 增加1。
- 初始化 ( f ) 数组:
- 初始时,每个数字的 ( f(x) ) 为其因子个数 ( d(x) )(即不分解的权值和)。
- 动态规划更新:
- 对于每个数字 ( i )(从2开始),枚举另一个数字 ( j )(从2开始),计算乘积 ( k = i \times j )。
- 如果 ( f[i] + f[j] > f[k] ),则更新 ( f[k] ) 为 ( f[i] + f[j] )(表示将 ( k ) 分解为 ( i ) 和 ( j ) 更优)。
- 处理查询:
- 对于每组查询,直接输出预处理得到的 ( f[x] )。
复杂度分析
- 预处理 ( d ) 数组:时间复杂度 ( O(n \log n) ),其中 ( n = 200000 )。
- 动态规划:时间复杂度 ( O(n \log n) ),因为内层循环次数是调和级数。
- 查询:每次查询 ( O(1) ),总体 ( O(T) )。
- 总复杂度:预处理 ( O(n \log n) ),查询 ( O(T) ),满足题目约束(( n \leq 200000 ), ( T \leq 10000 ))。
2. 小红的子序列逆序对
问题分析
我们需要计算一个数组中所有子序列的逆序对数量之和。逆序对的定义是:在一个子序列中,如果前面的元素大于后面的元素,则这两个元素构成一个逆序对。由于子序列可以不连续,我们需要考虑所有可能的子序列。
关键思路
- 逆序对的贡献:对于数组中的任意两个元素 (a_i) 和 (a_j)((i < j)),如果 (a_i > a_j),则这对元素在所有包含它们的子序列中都会贡献一个逆序对。
- 子序列包含这对元素的概率:对于任意子序列,包含 (a_i) 和 (a_j) 的概率是 ( \frac{1}{2^{j-i+1}} ),但实际上更准确的计算是:对于所有子序列,包含 (a_i) 和 (a_j) 的子序列数量是 (2^{n - (j - i + 1)})。
- 总逆序对数量:对于所有满足 (i < j) 且 (a_i > a_j) 的 ((i, j)) 对,计算它们对所有子序列的贡献之和。
数学推导
对于每一对 ((i, j)) 满足 (i < j) 且 (a_i > a_j),它们在所有子序列中的贡献是:
- 包含 (a_i) 和 (a_j) 的子序列数量为 (2^{n - 2})(因为除了 (a_i) 和 (a_j) 之外的其他 (n - 2) 个元素可以任意选或不选)。
- 因此,这对 ((i, j)) 的贡献是 (2^{n - 2})。
因此,总逆序对数量之和为:
[ \text{Total} = \sum_{i < j, a_i > a_j} 2^{n - 2} ]
优化计算
我们需要高效地计算满足 (i < j) 且 (a_i > a_j) 的对数。这可以通过归并排序或**树状数组(Fenwick Tree)**在 (O(n \log n)) 时间内完成。
C++ 解决方案
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MOD = 1e9 + 7;
// 树状数组类
class FenwickTree {
private:
vector<int> tree;
int size;
public:
FenwickTree(int n) : size(n), tree(n + 1, 0) {}
void update(int idx, int delta) {
while (idx <= size) {
tree[idx] += delta;
idx += idx & -idx;
}
}
int query(int idx) {
int res = 0;
while (idx > 0) {
res += tree[idx];
idx -= idx & -idx;
}
return res;
}
};
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
// 离散化
vector<int> temp = a;
sort(temp.begin(), temp.end());
temp.erase(unique(temp.begin(), temp.end()), temp.end());
for (int i = 0; i < n; ++i) {
a[i] = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin() + 1;
}
// 计算逆序对数量
FenwickTree ft(temp.size());
long long inv_count = 0;
for (int i = n - 1; i >= 0; --i) {
inv_count += ft.query(a[i] - 1);
ft.update(a[i], 1);
}
// 计算总贡献
long long power = 1;
for (int i = 0; i < n - 2; ++i) {
power = (power * 2) % MOD;
}
long long total = (inv_count % MOD) * (power % MOD) % MOD;
cout << total << endl;
return 0;
}
C 语言解决方案
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MOD 1000000007
#define MAXN 100005
typedef struct {
int val;
int idx;
} Pair;
int cmp(const void *a, const void *b) {
return ((Pair *)a)->val - ((Pair *)b)->val;
}
int unique(Pair *arr, int n) {
if (n == 0) return 0;
int j = 0;
for (int i = 1; i < n; i++) {
if (arr[i].val != arr[j].val) {
arr[++j] = arr[i];
}
}
return j + 1;
}
int fenwick[MAXN];
void update(int idx, int delta, int size) {
while (idx <= size) {
fenwick[idx] += delta;
idx += idx & -idx;
}
}
int query(int idx) {
int res = 0;
while (idx > 0) {
res += fenwick[idx];
idx -= idx & -idx;
}
return res;
}
int main() {
int n;
scanf("%d", &n);
int a[MAXN];
Pair temp[MAXN];
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
temp[i].val = a[i];
temp[i].idx = i;
}
// 离散化
qsort(temp, n, sizeof(Pair), cmp);
int m = unique(temp, n);
for (int i = 0; i < n; i++) {
int left = 0, right = m - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (temp[mid].val == a[i]) {
a[i] = mid + 1;
break;
} else if (temp[mid].val < a[i]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
// 计算逆序对数量
memset(fenwick, 0, sizeof(fenwick));
long long inv_count = 0;
for (int i = n - 1; i >= 0; i--) {
inv_count += query(a[i] - 1);
update(a[i], 1, m);
}
// 计算总贡献
long long power = 1;
for (int i = 0; i < n - 2; i++) {
power = (power * 2) % MOD;
}
long long total = (inv_count % MOD) * (power % MOD) % MOD;
printf("%lld\n", total);
return 0;
}
代码说明
- 离散化:将数组元素映射到连续的整数范围,以便使用树状数组。
- 树状数组:用于高效计算逆序对数量。
- 逆序对计算:从后往前遍历数组,利用树状数组查询当前元素之前比它小的元素数量。
- 贡献计算:总逆序对数量乘以 (2^{n-2}) 并对结果取模。
复杂度分析
- 离散化:(O(n \log n))。
- 逆序对计算:(O(n \log n))。
- 总复杂度:(O(n \log n)),适用于 (n \leq 10^5)。
3. 小美的彩带
参考链接:
美团2024年秋招第一场笔试(小美的彩带题解)-优快云博客
#include <bits/stdc++.h>
using namespace std;
#define all(x) x.begin(),x.end()
int n, q, len, m, blo;
char op;
int length;
int lisan[200005];
int a[400005], ans[200005];
int mp[200005], siz;
struct stuct {
int l, r, idx;
bool operator<(const stuct& x) const { //如果超时了可以试试这种
if (l / blo != x.l / blo) return l < x.l;
return (l / blo) & 1 ? r > x.r : r < x.r;
}
} b[200005];
int get(int v) { //离散的具体实现方式因人而异
return lower_bound(lisan + 1, lisan + 1 + m, v) - lisan;
}
int main() {
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
lisan[i] = a[i];
}
sort(lisan + 1, lisan + 1 + n);
m = unique(lisan + 1, lisan + 1 + n) - lisan;
--m;
blo = sqrt(2 * n) * 1.5;
for (int i = 1; i <= n; ++i) { //离散化
a[i] = get(a[i]);
a[i + n] = a[i];
}
scanf("\n");
int l = 1, r = 2 * n, len =
0; //维护一下l,r才知道彩带边缘的下标
for (int i = 1; i <= q; ++i) {
scanf("%c %d\n", &op, &length);
if (length >= n) {
ans[i] = m;
length %= n;
if (op == 'L') {
l += length;
if (l > n)l -= n;
} else {
r -= length;
if (r <= n)r += n;
}
continue;
}
if (op == 'L') {
++len;
b[len].l = l, b[len].r = l + length - 1, b[len].idx = i;
l += length;
if (l > n)l -= n;
} else {
++len;
b[len].l = r - length + 1, b[len].r = r, b[len].idx = i;
r -= length;
if (r <= n)r += n;
}
}
sort(b + 1, b + 1 + len);
l = 0, r = 0;
for (int i = 1; i <= len; ++i) {
while (r < b[i].r) {
++r;
if (mp[a[r]] == 0)siz++;
mp[a[r]]++;
}
while (r > b[i].r) {
mp[a[r]]--;
if (mp[a[r]] == 0)siz--;
--r;
}
while (l < b[i].l) {
mp[a[l]]--;
if (mp[a[l]] == 0)--siz;
++l;
}
while (l > b[i].l) {
--l;
if (mp[a[l]] == 0)++siz;
mp[a[l]]++;
}
ans[b[i].idx] = siz;
}
for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
return 0;
}
4. 小美和大富翁
// Created by YuanGodFollower
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
const int INF = 1e9 + 10;
vector<int> cards = {1,2,3,4};
bool vis[5];
int a[N];
ll dfs(int bias, int targ, int cur, ll val)
{
if(cur > targ) return -1;
if(cur == targ) return val;
ll ret = -1;
for(auto&& ca : cards)
{
if(!vis[ca])
{
if(val + a[bias + cur + ca] < 0) continue;
vis[ca] = 1;
ret = max(ret, dfs(bias, targ, cur + ca, val + a[bias + cur + ca]));
vis[ca] = 0;
}
}
return ret;
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
int n;cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
ll ans = 0;
for(int i = 0;i < n;i+=10)
{
int targ = min(10,n - i);
ans = dfs(i, targ, 0, ans);
if(ans == -1)
{
cout<<-1;
return 0;
}
}
cout<<ans;
}
5. 小红的数组删除
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <cmath>
#include <queue>
#include <iomanip>
#include <algorithm>
using namespace std;
unordered_map<long long, long long> hashMap;
long long a[200005];
void solve() {
hashMap.clear();
long long n, k, x;
cin >> n >> k >> x;
for (long long i = 1; i <= n; i++) {
cin >> a[i];
hashMap[a[i]]++;
}
long long minMiss = 0; // 未出现过的最小非负整数
while (true) {
auto it = hashMap.find(minMiss);
if (it == hashMap.end()) {
break;
}
minMiss++;
}
long long result = k * minMiss; // 一开始就删除整个数组
for (long long i = 1; i <= n; i++) {
long long start = 0;
long long mex = -1;
// 注意不能直接删除a[i],因为数组中可能会有重复的值
auto target = hashMap.find(a[i]);
if (target->second >= 2) {
hashMap[a[i]]--;
} else {
hashMap.erase(a[i]);
}
while (true) {
auto it = hashMap.find(start);
if (it == hashMap.end()) {
mex = start;
break;
}
start++;
}
// k代表删除整个数组的花费系数
// x代表删除单个元素的花费
result = min(result, i * x + k * mex);
}
cout << result << endl;
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
long long T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
6. 小红的数字删除
#include <iostream>
#include <vector>
using namespace std;
int main() {
int t;
cin >> t;
string n;
while (t--) {
cin >> n;
vector<int> vMod(3, 0);//vMod[i]表示原数各位数字中除以3余数为i的个数
for (char& c : n) {
++vMod[(c - '0') % 3];
}
int m = (vMod[1] + vMod[2] + vMod[2]) % 3; //原数除以3的余数
int ans;
if (m == 0) { //原数为3的倍数
ans = vMod[0];
if (ans == n.size()) --ans;
} else { //原数不是3的倍数
if (vMod[m] == 0) { //没有任何一位数字跟原数模3同余
ans = 0;
} else {
ans = vMod[0] + 1; //先删掉同余数字,再删余数为0的
if (ans == n.size()) --ans;
if (vMod[m] == 1 && (n[0] - '0') % 3 == m) {
for (int i = 1; i < n.size(); ++i) {
if (n[i] != '0') break;
--ans;
}
}
}
}
cout << ans << endl;
}
}
7. 小红的数字串
#include <iostream>
#include <string>
using namespace std;
int countSubstrings(const string &s, const string &k) {
int len_s = s.size();
int len_k = k.size();
int count = 0;
for (int l = 1; l <= len_s; ++l) {
if (l < len_k) {
count += len_s - l + 1;
} else if (l == len_k) {
for (int i = 0; i <= len_s - l; ++i) {
bool less = false;
for (int j = 0; j < l; ++j) {
if (s[i + j] < k[j]) {
less = true;
break;
} else if (s[i + j] > k[j]) {
break;
}
}
if (less) {
++count;
}
}
}
}
return count;
}
int main() {
string s, k_str;
cin >> s >> k_str;
cout << countSubstrings(s, k_str) << endl;
return 0;
}
8. 小红的01串
#include<bits/stdc++.h>
signed main() {
std::string s;
std::cin>>s;
int ans=0;
for(int i=0;i<s.size();i++){
if(s.substr(i,3)=="101"){
ans++;
if(s.substr(i+2,3)!="110")s[i+2]='0';
else s[i+1]='1';
}
if(s.substr(i,3)=="010"){
ans++;
if(s.substr(i+2,3)!="001")s[i+2]='1';
else s[i+1]='0';
}
}
std::cout<<ans<<'\n';
return 0;
}
9. 小红的爆炸串
#include <iostream>
#include <string>
using namespace std;
long long countNonExplodingSubstrings(const string &s, int k) {
int n = s.size();
long long count = 0;
int left = 0;
int diff_pairs = 0;
for (int right = 1; right < n; ++right) {
if (s[right] != s[right - 1]) {
diff_pairs++;
}
while (diff_pairs >= k) {
if (s[left] != s[left + 1]) {
diff_pairs--;
}
left++;
}
count += right - left;
}
return count + n; // 加上所有长度为1的子串
}
int main() {
int n, k;
cin >> n >> k;
string s;
cin >> s;
cout << countNonExplodingSubstrings(s, k) << endl;
return 0;
}
10. 小红的拼图
// 横向扫描 + 竖向扫描
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
// 对于每块拼图而言:在各个方向上的合法的搭档
vector<unordered_set<char>>WLegal = {{'W', '*'}, {'D', 'A', 'W', '*'}, {'A', '*'}, {'D', '*'}}; // {U D L R}
vector<unordered_set<char>>DLegal = {{'W', '*'}, {'S', '*'}, {'S', 'D', 'W', '*'}, {'D', '*'}};
vector<unordered_set<char>>SLegal = {{'D', 'S', 'A', '*'}, {'S', '*'}, {'A', '*'}, {'D', '*'}};
vector<unordered_set<char>>ALegal = {{'W', '*'}, {'S', '*'}, {'A', '*'}, {'W', 'S', 'A', '*'}};
unordered_map<char, vector<unordered_set<char>>>Frame = {{'W', WLegal}, {'D', DLegal}, {'S', SLegal}, {'A', ALegal}};
int main() {
int n;
cin >> n;
int temp_n = n;
while (n--) {
int row, col;
cin >> row >> col;
vector<vector<char>>Record(row, vector<char>(col));
int index = 0;
int temp_row = 0;
int temp_col = col;
while (row--) {
while (temp_col--) {
cin >> Record[temp_row][index++];
}
index = 0;
temp_col = col;
temp_row++;
}
bool flag = true;
for (int i = 0; i < Record.size(); i++) {
if (flag == false)break;
for (int j = 1; j < Record[i].size(); j+=2) {
if (flag == false)break;
if (Record[i][j] == '*')
continue;
if (j == Record[i].size() - 1) {
if (Frame[Record[i][j]][2].find(Record[i][j - 1]) ==
Frame[Record[i][j]][2].end())flag = false;
} else {
if (Frame[Record[i][j]][2].find(Record[i][j - 1]) ==
Frame[Record[i][j]][2].end())flag = false;
if (Frame[Record[i][j]][3].find(Record[i][j + 1]) ==
Frame[Record[i][j]][3].end())flag = false;
}
}
}
if (flag == false) {
cout << "No" << endl;
continue;
}
for (int i = 1; i < Record.size(); i+=2) {
if (flag == false)break;
for (int j = 0; j < Record[i].size(); j++) {
if (flag == false)break;
if (Record[i][j] == '*')
continue;
if (i == Record.size() - 1) {
if (Frame[Record[i][j]][0].find(Record[i - 1][j]) == Frame[Record[i][j]][0].end())flag = false;
} else {
if (Frame[Record[i][j]][0].find(Record[i - 1][j]) == Frame[Record[i][j]][0].end())flag = false;
if (Frame[Record[i][j]][1].find(Record[i + 1][j]) == Frame[Record[i][j]][1].end())flag = false;
}
}
}
if (flag == false) {
cout << "No" << endl;
} else {
cout << "Yes" << endl;
}
}
return 0;
}