2024.11.26 周二日常
2024.11.26 周二日常
-
Q1. 1200
给定一数组k(代表n个人的倍率),设在每个人上投资为xi,若其胜利则获k*xi,最终一人胜利。问是否可以保证无论谁胜利,收益大于总投资的方案。(n<=20,k<=50) -
Q2. 1300
给定一数组,问最小的k使所有长度为k的区间按位或相等。 -
Q3. 1500
给定一数组,定义MAD:数组中出现大于一次且最大的数。操作直到数组全为0:每次操作i:1->n a[i]=MAD{a[i]的前缀数组}。问所有操作前数组和的总和。 -
A1. 补
解法1. 二分总投资s,若总投资为x,则x+1也可满足,呈单调性。检查:给每人合法的最小投资判断总投资sum<=x。找到最小合法s,进行分配。
解法2. 投入1/ki可获1,需满足:求和1/ki<1 两边乘lcm。判断每人投资lcm/k[i]是否合法。 -
A2. 48mins 读错题硬控20min,st板子不完善
二分加ST表:若x合法,则x+1一定合法,k属于[x,n]。每次检查使用ST表查询区间按位或依次判断。 -
A3. 补 抽象问题造样例引导思考 样例不全面导致思考不全面
观察发现题吧?造一个好数据,模拟发现关键点:1.操作后数组单调不降 2.操作1次可能有单个元素段,操作2次即为多个连续相同元素子数组。3.然后每次操作就是向右平移,找一个好的计算方法也很重要:根据到n+1位置的距离计算每个元素出现的次数。
A1.
#include <bits/stdc++.h>
#define int long long //
#define endl '\n' //
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
const int mod = 998244353;
const int N = 10 + 5e5;
void _();
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
_();
return 0;
}
// 给定一数组k(代表n个人的倍率),设在每个人上投资为xi,若其胜利则获k*xi,最终一人胜利。问是否可以保证无论谁胜利,收益大于总投资的方案。(n<=20,k<=50)
// 1. 二分总投资s,若总投资为x,则x+1也可满足,呈单调性。检查:给每人合法的最小投资判断总投资sum<=x。找到最小合法s,进行分配。
// 2. 投入1/ki可获1,需满足:求和1/ki<1 两边乘lcm。判断每人投资lcm/k[i]是否合法。
// 2.数学
void _()
{
int n;
cin >> n;
vector<int> k(n);
for (int &_k : k)
cin >> _k;
auto lcm = [](int a, int b)
{
return a / __gcd(a, b) * b;
};
int L = k.front();
for (int i = 1; i < n; i++)
L = lcm(L, k[i]);
int s = 0;
for (auto k : k)
s += L / k;
if (s >= L)
{
cout << -1 << endl;
return;
}
for (auto k : k)
cout << L / k << ' ';
cout << endl;
}
// 1.二分
// void _()
// {
// int n;
// cin >> n;
// vector<int> k(n);
// for (int &_k : k)
// cin >> _k;
// int l = 0, r = 1e9 * n + 1;
// auto ok = [&](int x)
// {
// int sum = 0;
// for (int i = 0; i < n; i++)
// sum += x / k[i] + 1;
// return sum <= x;
// };
// while (r - l - 1)
// {
// int mid = l + r >> 1;
// if (ok(mid))
// r = mid;
// else
// l = mid;
// }
// if (ok(r))
// {
// for (int i = 0; i < n; i++)
// cout << r / k[i] + 1 << ' ';
// cout << endl;
// }
// else
// cout << -1 << endl;
// }
A2.
#include <bits/stdc++.h>
#define int long long //
#define endl '\n' // 交互/调试 关
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
void _();
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
_();
return 0;
}
// 给定一数组,问最小的k使所有长度为k的区间按位或相等。
// 二分加ST表:若x合法,则x+1一定合法,k属于[x,n]。每次检查使用ST表查询区间按位或依次判断。
// 48min
void _()
{
int n;
cin >> n;
vector<int> a(n + 1);
vector<vector<int>> dp(n + 1, vector<int>(32)); // 内存超限 必要时关long long
for (int i = 1; i <= n; i++)
cin >> a[i];
// init
for (int j = 0; j < 30; j++) // j 是每一层状态
for (int i = 1; i <= n; i++)
{
if (i + (1 << j) - 1 > n)
continue;
if (!j)
dp[i][j] = a[i];
else
dp[i][j] = dp[i][j - 1] | dp[i + (1 << j - 1)][j - 1];
}
// query
auto ask = [&](int l, int r)
{
int k = log(r - l + 1) / log(2);
return dp[l][k] | dp[r + 1 - (1 << k)][k];
};
auto ok = [&](int x)
{
int ans = 0;
for (int i = 1; i + x - 1 <= n; i++)
{
auto re = ask(i, i + x - 1);
if (i == 1)
ans = re;
else
{
if (re - ans)
return 0;
}
}
return 1;
};
// bug(111);
int l = 0, r = n;
while (r - l - 1)
{
int mid = l + r >> 1;
if (ok(mid))
r = mid;
else
l = mid;
}
cout << r << endl;
}
A3.
#include <bits/stdc++.h>
#define int long long //
#define endl '\n' // 交互/调试 关
using namespace std;
#define bug(BUG) cout << "bug:# " << (BUG) << endl
#define bug2(BUG1, BUG2) cout << "bug:# " << (BUG1) << " " << (BUG2) << endl
#define bug3(BUG1, BUG2, BUG3) cout << "bug:# " << (BUG1) << ' ' << (BUG2) << ' ' << (BUG3) << endl
void _();
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while (T--)
_();
return 0;
}
// 抽象问题造样例引导思考 样例不全面导致思考不全面
// 给定一数组,定义MAD:数组中出现大于一次且最大的数。操作直到数组全为0:每次操作i:1->n a[i]=MAD{a[i]的前缀数组}。问所有操作前数组和的总和。
// 观察发现题吧?造一个好数据,模拟发现关键点:1.操作后数组单调不降 2.操作1次可能有单个元素段,操作2次即为多个连续相同元素子数组。
// 3.然后每次操作就是向右平移,找一个好的计算方法也很重要:根据到n+1位置的距离计算每个元素出现的次数。
void _()
{
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
auto f = [&]()
{
int sum = 0, tmax = 0;
map<int, int> cnt;
for (int i = 1; i <= n; i++)
{
sum += a[i];
cnt[a[i]]++;
if (cnt[a[i]] > 1)
tmax = max(tmax, a[i]);
a[i] = tmax;
}
return sum;
};
int res = f() + f();
for (int i = n; i; i--)
res += a[i] * (n - i + 1);
cout << res << endl;
}
// // 34mins-36mins-55mins-补
// void _()
// {
// int n;
// cin >> n;
// int sum = 0;
// vector<int> a(n + 1);
// for (int i = 1; i <= n; i++)
// cin >> a[i], sum += a[i];
// int tmax = 0;
// map<int, int> cnt;
// map<int, int> ans_cnt;
// for (int i = 1; i <= n; i++)
// {
// cnt[a[i]]++;
// if (cnt[a[i]] > 1)
// tmax = max(tmax, a[i]);
// if (tmax)
// ans_cnt[tmax]++;
// }
// vector<pair<int, int>> ans;
// for (auto [x, ct] : ans_cnt)
// ans.push_back({x, ct});
// sort(ans.rbegin(), ans.rend());
// int pre = 0, res = sum;
// for (int i = 0; i < ans.size(); i++)
// {
// auto [x, k] = ans[i];
// // bug2(x, k);
// res += pre * x * k;
// if (k - 1)
// pre += k;
// if (k == 1 && pre && i) // 坑点
// continue;
// res += x * k * (k + 1) >> 1;
// }
// // bug(tmax);
// cout << res << endl;
// }

浙公网安备 33010602011771号