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;
}
posted @ 2025-07-08 01:25  Li_Yujia  阅读(129)  评论(0)    收藏  举报