Codeforces Round 1036 CD
C. Subset Multiplication
题意:
找到一个可能的x值,使得与任意数组元素相乘以后 \(a[i + 1] \equiv 0 \pmod{a[i]}\)。
暴力思路:
遇到下降就把差值累积成 x 的质因子。
算法的时间复杂度为 $ O\left(\sqrt{b} \cdot n\right) \approx 2 \times 10^{9}$,会TLE。
代码
#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;
const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
ll Cpow (ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
void solve() {
int n;
cin >> n;
vector<int> a (n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
map<ll, ll> res;
ll ans = 1;
map<ll, ll> pre;
for (int i = 2; i <= a[1] / i; ++i) {
while (a[1] % i == 0) {
a[1] /= i;
pre[i] ++;
}
}
if (a[1]) pre[a[1]] ++;
for (int i = 2; i <= n; ++i) {
map<ll, ll> ne;
int y = a[i];
for (int j = 2; j <= y / j; ++j) {
while (y % j == 0) {
y /= j;
ne[j] ++;
}
}
if (y) ne[y] ++;
for (auto j : pre) {
res[j.first] = max (res[j.first], j.second - ne[j.first]);
}
pre = ne;
}
for (auto i : res) {
if (i.second) ans = ans * (Cpow (i.first, i.second));
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio (false);
cin.tie(NULL);
cout.tie(NULL);
t = 1;
cin >> t;
while (t --) {
solve();
}
return 0;
}
正确思路
其实根本 不需要分解质因数。
利用「漂亮数组」性质 \(a_i \mid a_{i+1}\) 可直接用 \(\gcd + \operatorname{lcm}\) 线性求出 \(x\)。
推导
对相邻的两个数 \(b_i, b_{i+1}\) 考虑四种情况(两者是否被乘 x):
情况 | Bob 是否乘以 \(x\) | 需要满足的整除关系 |
---|---|---|
① 都没乘 | \(b_i \mid b_{i+1}\) | - |
② 只乘 \(b_i\) | \(\frac{b_i}{x} \mid b_{i+1} \Rightarrow x \mid \frac{b_i}{\gcd(b_i, b_{i+1})}\) | - |
③ 只乘 \(b_{i+1}\) | \(b_i \mid \frac{b_{i+1}}{x} \Rightarrow x \mid \frac{b_{i+1}}{\gcd(b_i, b_{i+1})}\) | - |
④ 两个都乘 | 同 ① | - |
于是 \(x\) 必须整除
\[r_i = \frac{b_i}{\gcd(b_i, b_{i+1})}, \qquad r'_i = \frac{b_{i+1}}{\gcd(b_i, b_{i+1})}.
\]
取所有 \(r_i\)(或对称的 \(r'_i\))的 最小公倍数 就一定满足所有约束,且只用到 \(\gcd\) 运算,时间复杂度 \(O(n \log M)\)。
代码
#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;
const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
ll lcm (ll a, ll b) {
ll q = __gcd (a, b);
return a * b / q;
}
void solve() {
int n;
cin >> n;
vector<int> a (n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
ll x = 1;
for (int i = 2; i <= n; ++i) {
x = lcm (x, a[i - 1] / __gcd (a[i], a[i - 1]));
}
cout << x << "\n";
}
int main() {
ios::sync_with_stdio (false);
cin.tie(NULL);
cout.tie(NULL);
t = 1;
cin >> t;
while (t --) {
solve();
}
return 0;
}
D. Make a Palindrome
题意:
对于一个序列,选定i,j之间第k小的数字删除,问最后序列能否成为一个回文串
思路:
考虑贪心,只能删除第k小的数,当选定区间为[1,n]时,对于a[i],若a[i] < a[k],则此数字无法被删除。故将数组元素分为三类:
①a[i] < a[k]
②a[i] = a[k]
③a[i] > a[k]
对于第③类,一定可以删除,因为它们在原串中即使不能构成回文,也可以删去,故直接不考虑。
对于第①类,若当前遍历的前后指针均为第①类时,两个元素均无法被删除,一定无法构成回文串。
对于第②类,我们可以选择性删除,以构成回文串,但应始终保证删除之前的数组元素个数是大于k的。
用双指针遍历一遍即可。
代码
#include<bits/stdc++.h>
#define ll long long
#define ce cerr
#define ull unsigned long long
#define lll __int128
using namespace std;
const int inf = 0x3f3f3f3f;
const ll iinf = 1e18;
//cin.ignore(std::numeric_limits< streamsize >::max(), '\n');
int t;
void solve() {
int n, k;
cin >> n >> k;
vector<int> a (n + 1);
vector<int> b (n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
b[i] = a[i];
}
sort (a.begin () + 1, a.end ());
int tar = a[k];
int cnt = 0;
int ccnt = 0;
vector<int> temp;
for (int i = 1; i <= n; ++i) {
if (b[i] <= tar) {
temp.push_back (b[i]);
if (b[i] != tar) ccnt ++;
}
}
int l = 0;
int r = temp.size () - 1;
ccnt = (int)temp.size() - k + 1;
while (l < r) {
if (temp[l] != temp[r] && temp[l] != tar && temp[r] != tar) {
cout << "NO" << "\n";
return ;
}else if (temp[l] == temp[r]) {
l ++;
r --;
}else if (temp[l] == tar) {
cnt ++;
if (cnt > ccnt) {
cout << "NO" << "\n";
return ;
}
l ++;
}else if (temp[r] == tar) {
cnt ++;
if (cnt > ccnt) {
cout << "NO" << "\n";
return ;
}
r --;
}
}
cout << "YES" << "\n";
}
int main() {
ios::sync_with_stdio (false);
cin.tie(NULL);
cout.tie(NULL);
t = 1;
cin >> t;
while (t --) {
solve();
}
return 0;
}