12.21 模拟赛
T1
题面:一个长度为 \(n\) 的序列 \(\{a_1,a_2,\cdots,a_n\}\),求有多少个不同的 \(\gcd(a_i,a_j)(i\not=j)\)。 \(n\le2\times 10^5,a_i\le 10^7\)
题解:从大到小枚举值域,简单容斥,复杂度是调和级数 \(O(n\ln n)。\)
死亡回放:\(N\) 开小了。
核心代码
for (int s = maxa; s >= 1; s--) {
int now = 0;
for (int i = s; i <= maxa; i += s) now += vis[i];
cnt[s] = 1ll * now * (now - 1) >> 1;
for (int i = 2 * s; i <= maxa; i += s) cnt[s] -= cnt[i];
if (cnt[s]) ans++;
}
T2
洛谷link
题面:有一个二进制的完全平方数,但是它其中有 \(k\) 位被替换成 \(?\),请你找回原来的数。\(len\le 125,k\le40\)。
题解:考虑折半(一半向上取整,两边其实长度一样),设该完全平方数为 \(A\),\(a=\sqrt A\),由于 \(k\le 40\),那么必然有一半 \(?\) 的个数是 \(\le 20\),我们考虑能否通过小的那边,确定答案。
我们通过高位确定答案
我们令已经确定的数位中1位置上的和为 \(x\),仍未确定的数位的位置上的和为 \(y\)。
于是显然有 \(\sqrt{x}\le a \le \sqrt{x+y}\),还有 \(x\ge 2^n,y\le 2^{\frac{n}{2}}\)。
于是我们只需要分别检查 \(\lfloor\sqrt{x}\rfloor,\lfloor\sqrt{x}\rfloor+1\) 即可,\(x\) 需要 \(2^\frac{k}{2}\) 枚举。
我们通过低位确定答案
完整代码
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define out() (cout << "sb\n")
#define i128 __int128
#define p(i) ((i128(1)) << i)
int T;
string s;
bool check(const i128 &x, const i128 &mask1, const i128 &mask0) {
return ((x | mask1) == x && (x & ~mask0) == x); //不多也不少=>刚刚好
}
i128 solve(const string &s) {
int n = s.length();
if (n == 1)
return s[0] == '0' ? -1 : 1;
if (s[0] != '1' && s[1] != '1') {
i128 ans = solve(s.substr(2));
if (~ans) return ans * 4;
}
i128 mask1 = 0, mask0 = 0;
for (int i = 0; i < n; i++) {
mask1 |= (s[i] == '1') * p(i);
mask0 |= (s[i] == '0') * p(i);
}
int mid = n + 1 >> 1, cntf = 0, cnts = 0;
for (int i = 0; i < mid; i++) cntf += (s[i] == '?');
for (int i = n - mid; i < n; i++) cnts += (s[i] == '?');
if (cntf < cnts) { //低位
i128 r0 = mask0, r1 = mask1;
vector<i128> v, now;
v.push_back(1);
for (int i = 2; i <= mid; i++) {
i128 mask = p(i) - 1;
mask0 = r0 & mask, mask1 = r1 & mask;
now.clear();
for (i128 &val : v) {
if (check(val * val, mask1, mask0)) now.push_back(val);
val |= p(i - 1);
if (check(val * val, mask1, mask0)) now.push_back(val);
}
v = now;
}
mask1 = r1, mask0 = r0;
for (i128 &val : v)
if (check(val * val, mask1, mask0)) return val * val;
} else { //高位暴力枚举
i128 mask = 0, base = 0;
for (int i = n - mid; i < n; i++) {
mask |= (s[i] == '?') * p(i);
base |= (s[i] == '1') * p(i);
}
i128 t = mask + 1;
while (t) {
t = (t - 1) & mask;
i128 val = floor(sqrt((long double)(t | base)));
if (check(val * val, mask1, mask0)) return val * val;
val++;
if (check(val * val, mask1, mask0)) return val * val;
}
}
return -1;
}
int main() {
// system("fc .out .out");
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
IOS;
cin >> T;
// T = 1;
for (int _ = 1; _ <= T; _++) {
cin >> s; int n = s.length();
if (n == 1) {
cout << "Case #" << _ << ": ";
cout << s << "\n";
continue;
}
reverse(s.begin(), s.end());
i128 ans = solve(s);
cout << "Case #" << _ << ": ";
for (int i = n - 1; ~i; i--)
cout << (int)((ans >> i) & 1);
cout << "\n";
}
return 0;
}
T3
题面:三维空间中有 \(n\) 个点,\(n\) 个向量,你要给每个点分配一个向量,使得分配后每两个点之间的距离 \(\ge\) 原来两个点距离,输出分配方案。
\(n\le 500\),\(-10^{4}\ge\)每个点和向量的坐标大小 \(\le 10^4\)。
题解:考虑动态调整,给每个点先分配一个向量。然后 \(O(n^2)\) 的枚举两个点,如果两个点之间的距离 \(\le\) 原来的距离,进行交换,在重新从头枚举,重复执行,直到满足条件。
核心代码
random_shuffle(p + 1, p + n + 1);
// for (int i = 1; i <= n; i++) cout << p[i] << " \n"[i == n];
while (1) {
bool flag = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
if (disa(i, j) > dis(i, j)) {
swap(p[i], p[j]);
flag = 1;
}
if (!flag) break;
}

浙公网安备 33010602011771号