https://codeforces.com/contest/1349
记错时间错过了。但是感觉可以补一下。一天一场div1(1/30)。
A - Orac and LCM
题意:给 (nin[1,100000]) 个数的数组 (a) ,(a_i) 范围在 ([1,200000]) 。定义一个集合 (s) 的 (gcd) 为被 (s) 中的所有数整除的最大的数,定义一个集合 (s) 的 (lcm) 为整除 (s) 中的所有数的最小的数。取出数组 (a) 的所有两两组合数数对的 (lcm) 记作集合 (t) ,求 (gcd(t)) 。
题解:就是说先取数对的 (lcm) 然后再取所有数的 (gcd) ,首先因为 (lcm) 每种质因子中最大的那个一定会出现在 (t) 的某个数的因子中,但是这好像没有用因为会被 (gcd) 去掉。
所以直接就是求所有数的 (gcd) ?观察第二个样例 (lcm(10,24)=120) ,确实包含因子 (40) ,答案并不是 (2) 。仔细一想这种题肯定是直接分解成质因数形式观察就可以了。
10 24 40 80
以质因子 (2) 的角度考察,得到
1 2 3 4
他们的 (lcm) 集合为
2 3 4 3 4 4
gcd的结果为
2
所以就是取这种质因数中最小的两个就可以了,考虑如何快速对这些数进行质因数分解,最简单的思路就是直接random_shuffle或者sort,然后验证所有的质因子 (k) ,若是某个质因子 (k) 有至少2个数不含 (k) 则直接break。注意到每个 (a_i) 至多有 (6) 种不同的质因子,应该大部分都是提前break的了,假如有某个质因子到很后才break,则这种质因子不应该超过十几种。
PS:溢出了,太久不打搞得离谱,还好没打呢,好演啊。
int n;
int a[100005];
bool vis[200005];
int check(int d) {
int mind1 = INF, mind2 = INF;
int cnt0 = 0;
for(int i = 1; i <= n; ++i) {
if(a[i] % d != 0) {
++cnt0;
if(cnt0 >= 2)
return 1;
}
int cnt = 0;
int x = a[i];
while(x % d == 0) {
++cnt;
x /= d;
}
if(cnt < mind2) {
mind2 = cnt;
if(mind2 < mind1)
swap(mind1, mind2);
}
}
int res = 1;
while(mind2--)
res *= d;
return res;
}
void TestCase() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
ll ans = 1;
for(int i = 2; i <= 200000; ++i) {
if(vis[i])
continue;
ans *= check(i);
for(int j = 2 * i; j <= 200000; j += i)
vis[j] = 1;
}
printf("%lld
", ans);
return;
}
另一种解法是,求出前缀gcd和后缀gcd,然后再全部搞一次lcm,这个看起来就太复杂了。
B - Orac and Medians
题意:给一个 (nin[1,100000]) 个数的数组 (a) ,每次操作可以选择一段连续区间,然后把这段区间的值全部替换成这段区间的中位数,假如区间的长度是偶数则选择小的那个(左)中位数。问是否能把数组 (a) 变为全部都是 (k) 。
(假的)题解:首先 (k) 必须要出现。然后,发现一个惊人的事实: (k) 可以“感染”两侧的比它大的数!而连续两个的 (k) 可以“感染”两侧的比它小的数!所以只要存在一个 (k) 的两侧至少有一个大于等于它的数,就是yes。
但这只是充分条件而不是必要条件,经过这个检测之后还暂时是no的话,则说明所有的 (k) 两侧都是比它小的数,但是可能可以通过选取一个区间使得 (k) 恰好成为(左)中位数。基于上面的分析,我们得知只要有一个长度至少为2的区间使得 (k) 为(左)中位数,答案就是yes。
这个可能要借助一下数据结构?把所有比 (k) 小的视作 (-1) ,把所有比 (k) 大的视作 (+1) ,好像变成了最大子数组和dp?
参考一下别人的:https://www.cnblogs.com/dysyn1314/p/12881386.html
再看一下官方题解,貌似是只要存在一对相邻的大于等于 (k) 的数,他们之间夹的小于 (k) 的数小于等于1,就可以构造。充分性是显然的:首先,选择这一对数作为左右边界,那么区间内的数都会变成大于等于 (k) ,然后选择其中的连续的两个大于等于 (k) ,再加上新的一个数,一定可以变成三个大于等于 (k) (也就是说可以向两侧扩展),若这两个数之中至少有一个是 (k) ,那么这就是一个构造,否则向两侧扩展之后必定会遇到 (k) 然后被 (k) 感染。
必要性的话就不是特别显然,若任意两个相邻的大于等于 (k) 的数之间夹了至少两个小于 (k) 的数,则无论怎么选择都只能把区间变成小于 (k) 的数。
这一场也太恶心了吧。
int n, k;
void TestCase() {
scanf("%d%d", &n, &k);
int prev_ge = -1, mindis = INF, exist_k = 0;
for(int i = 1, a; i <= n; ++i) {
scanf("%d", &a);
if(a >= k) {
if(prev_ge != -1)
mindis = min(mindis, i - prev_ge);
prev_ge = i;
if(a == k)
exist_k = 1;
}
}
if(exist_k == 1 && (n == 1 || mindis <= 2))
puts("yes");
else
puts("no");
return;
}