1111上午考试总结
1111考试总结
T1
题目大意:
有 n 个小朋友,编号为 1 到 n,他们每人写了一封信,放到了一个信箱里,接下来每个人从中抽取一封书信。显然,这样一共有 n!种拿到书信的情况。现在亮规定,对任意的 1<=x,y<=n,如果 x 号小朋友拿到 u 号小朋友写的书信,y 号小朋友拿到 v 号小朋友写的书信,那么(x+y)号小朋友必须拿到(u+v)号小朋友写的书信(这里的加法若和超过了 n,那么就减去 n)。小林想知道,总共有多少种拿到书信的情况呢? \(n <= 1e15\)
欧拉函数.
简化题目: 求1到n与n互质的数, 也就是\(\phi (n)\).
为什么呢?打标找规律.
如果1拿到\(x\)的信, 那么2就必须拿\(2x\)的信, 那么\(y\)就必须拿到\(yx\)的信.我们需要让\(x, 2x, ..., nx\)(大于n的就减n)恰好取遍\(1, 2, ..., n\),发现\(x\)与\(n\)互质才成立.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
long long n, cnt, ans, phi;
void work() {
phi = n;
for(int i = 2;i * i <= n; i++) {
if(!(n % i)) phi = phi / i * (i - 1);
while(!(n % i)) n /= i;
}
if(n > 1) phi = phi / n * (n - 1);
}
int main() {
n = read();
if(n == 4) printf("4");
else { work(); printf("%lld\n", phi); }
fclose(stdin); fclose(stdout);
return 0;
}
T2
题目大意:
小林在一家商店里购物,共有 i 件物品,第 i 件物品的价格为 i。小林身上带了许多金条,每根金条的价值都为整数,价值为 1~N 的金条数量都足够多。对于任何一件物品,小林只会用同一价值的若干金条购买它,而且不能找零。因此,对于第 i 件物品,会有 C(i)种购买方法。小林只会选择这样的一件物品 i进行购买:C(i) > max{ C(j) }, 1 <= j < i。请你告诉小林,他能购买的最贵的物品的价格是多少。\(n <= 2e9\)
题目链接
缩索.
设\(x = p_1^{k_1}*p_2^{k_2}*p_3^{k_3}*...*p_c^{k_c}\),\(p\)单调递增.
假设\(x > y, k_x > k_y\), 发现交换\(k_x, k_y\)会得到更优的答案, 所以\(k\)一定是单调不增的.
举个例子:\(2^2 * 3^3\)与\(2^3*3^2\)的\(C\)相同, 但是后者明显更优.
然后缩索就好了.前13个素数相乘已经大于\(2e9\)了, 所以只用前13个素数就好.
#include <bits/stdc++.h>
using namespace std;
int n, cnt, ans, maxn;
int prime[14] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
void dfs(int sum, int num, int now, int last) {
if(num > maxn || (num == maxn && sum < ans)) maxn = num, ans = sum;
long long tmp = sum;
for(int i = 1;i <= last; i++) {
tmp *= prime[now];
if(tmp > n) break; //剪枝
dfs(tmp, num * (i + 1), now + 1, i);
if(n / tmp < prime[now]) break; //剪枝
}
}
int main() {
cin >> n; dfs(1, 1, 1, 60);
printf("%lld\n", ans);
return 0;
}
T3
题目大意:
n 个小朋友在一起做游戏,第 i 个小朋友的快乐值为 Di。当第 i 个小朋友和第 j 个小朋友一起玩时,他们能获得 Di xor Dj 的快乐值。每一个小朋友 i 都想知道,他和谁一起玩能够获得最大的快乐值,请你帮他们每个人分别求出这个值。 \(n <= 2e6\)
给出\(K, B, P\), 根据\(D_i = (KD_{i - 1} + B) \% 2 ^ {24}, D_1 = P\)自行算出D数组.
Tire树.md考场上竟然没想出来
01Tire板子. 然后按题意模拟, 看代码就好了.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2e6 + 5;
int n, K, B, mod, cnt, tot, pow_3;
int a[N], ans[N], t[N][2];
void ins(int x) {
int p = 0;
for(int i = 24;i >= 0; i--) {
int y = (x >> i) & 1;
if(!t[p][y]) t[p][y] = ++ cnt;
p = t[p][y];
}
}
int query(int x) {
int p = 0, res = x;
for(int i = 24;i >= 0; i--) {
int y = (x >> i) & 1;
if(t[p][!y]) { p = t[p][!y]; if(!y) res ^= (1 << i); }
else { p = t[p][y]; if(y) res ^= (1 << i); }
}
return res;
}
int main() {
mod = pow(2, 24);
n = read(); K = read(); B = read(); a[1] = read();
for(int i = 2;i <= n; i++) a[i] = (1ll * K * a[i - 1] % mod + B) % mod;
for(int i = 1;i <= n; i++) ins(a[i]);
for(int i = 1;i <= n; i++) ans[i] = query(a[i]);
pow_3 = 1;
for(int i = 1;i <= n; i++) tot = (tot + 1ll * pow_3 * ans[i] % mod) % mod, (pow_3 *= 3) %= mod;
printf("%d", tot);
return 0;
}