传奇构造场。
A. Carnival Wheel
time limit per test: 1 second
memory limit per test: 256 megabytes
You have a prize wheel divided into l l l sections, numbered from 0 0 0 to l − 1 l-1 l−1. The sections are arranged in a circle, so after section l − 1 l-1 l−1, the numbering continues again from section 0 0 0.
Initially, the prize pointer is at section a a a. Each time you spin the wheel, the pointer moves exactly b b b sections forward. That is, after one spin, the pointer moves from section a a a to section ( a + b ) m o d l (a+b)\bmod l (a+b)modl, after two spins to ( a + 2 b ) m o d l (a+2b)\bmod l (a+2b)modl, and so on ∗ ^{\text{∗}} ∗.
You may spin the wheel any number of times (including zero). After you stop, the section where the pointer finally lands determines your prize: you receive an amount equal to the number of that section.
What is the maximum prize you can obtain?
∗ ^{\text{∗}} ∗Here, x m o d y x \bmod y xmody denotes the remainder from dividing x x x by y y y.
经典gcd
cpp
void solve()
{
int l, a, b;
cin >> l >> a >> b;
int g = gcd(l, b);
if (g == 1) {
cout << l - 1 << endl;
return;
} else {
int ans = a;
for (int i = 1; i <= l; i++) {
ans = max((i * b + a) % l, ans);
}
cout << ans << endl;
return;
}
}
B. Ashmal
time limit per test: 1 second
memory limit per test: 256 megabytes
You have an array a a a of n n n strings a 1 , a 2 , ... , a n a_{1}, a_{2}, \ldots, a_{n} a1,a2,...,an, each consisting of lowercase English letters, and an empty string s s s.
In the i i i-th ( 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n) step, you should do one of the following:
- add a i a_{i} ai to the beginning of s s s, or
- add a i a_{i} ai to the end of s s s.
For example, if before the i i i-th step s = a b a s = \mathtt{aba} s=aba and a i = b b a a_{i} = \mathtt{bba} ai=bba, after the i i i-th step, s s s will be equal to a b a b b a \mathtt{ababba} ababba or b b a a b a \mathtt{bbaaba} bbaaba.
What's the lexicographically smallest string s s s you can reach after n n n steps?
A string a a a is lexicographically smaller than a string b b b of the same length, if and only if the following holds:
- in the first position where a a a and b b b differ, the string a a a has a letter that appears earlier in the alphabet than the corresponding letter in b b b.
简单贪心
cpp
void solve()
{
int n;
cin >> n;
vector<string> ss(n + 1);
for (int i = 1; i <= n; i++) {
cin >> ss[i];
}
string ans ;
for (int i = 1; i <= n; i++) {
string ans1 = ans + ss[i], ans2 = ss[i] + ans;
ans = min(ans1, ans2);
}
cout << ans << endl;
}
C. XOR-factorization
time limit per test: 2 seconds
memory limit per test: 256 megabytes
Ostad thinks that the usual way of factoring numbers is too mathematical, so he invented a new notion called XOR-factorization, which is more computer-science-like. For a given integer n n n, a sequence of integers a 1 , a 2 , ... , a k a_1, a_2, \ldots, a_k a1,a2,...,ak with 0 ≤ a i ≤ n 0 \le a_i \le n 0≤ai≤n for all i i i is called a XOR-factorization of n n n if and only if
a 1 ⊕ a 2 ⊕ ⋯ ⊕ a k = n , a_1 \oplus a_2 \oplus \cdots \oplus a_k = n, a1⊕a2⊕⋯⊕ak=n,
where ⊕ \oplus ⊕ denotes the bitwise XOR operation.
You are given integers n n n and k k k. Find a XOR-factorization a 1 , a 2 , ... , a k a_1, a_2, \ldots, a_k a1,a2,...,ak of n n n that maximizes the sum a 1 + a 2 + ⋯ + a k a_1 + a_2 + \cdots + a_k a1+a2+⋯+ak.
It can be proven that under the problem conditions, a XOR-factorization always exists.
传奇构造题。
这里官方题解中提到了一个很经典的思考方式,常用于数位DP ,就是松 / 紧的思想,简单来说:
- 对于一个二进制数字 x x x 如: ( 1011000 ) 2 (1011000)_2 (1011000)2
- 对于目前由高到低枚举到的第 b 位,如果 b 位为 1 (x >> bit & 1),则如果这一位置我取 0,则后面可以随便取 1,大小也不会更大,这就是松,反之就是紧。
对于这道题,分奇偶讨论:
- 奇数情况很简单,直接输出 k k k 个 n n n 即可,这种情况一定是和最大的。
- 偶数情况就很复杂了:
- 首先要想到,对于数字 n n n 的第 bit 位,如果这位是 1 1 1,则最后 k k k 个数字中必定会有奇数 个数字在这一位置取 1 1 1;如果这位是 0 0 0,则最后 k k k 个数字中必定会有偶数 个数字在这一位置取 1 1 1;
- 所以,我们想要让取到的 1 1 1 尽可能多,且 k k k 个数字中的每个数字都满足小于 n n n 的条件。
- 对于当前二进制位是 1 1 1 的,我们必定会只选择 k − 1 k - 1 k−1 个数字这一位构造为 1 1 1,则剩下的这个数字就满足我们一开始提到的 松 的情况了,则对于这个 松 的数字,我就可以选择在它 后面 (较低位)的 0 的位置都填上 1 1 1 (当然得满足最后当前位置为 1 的总个数为偶数)。
所以就有如下代码:
cpp
void solve()
{
int n, k;
cin >> n >> k;
if (k & 1) {
for (int i = 1; i <= k; i++) {
cout << n << ' ';
}
cout << endl;
} else {
vector<int> ans(k + 1);
int num = 0; // 松的数字的个数
for (int bit = __lg(n); bit >= 0; bit--) {
if (n >> bit & 1) {
for (int i = 1; i <= k; i++) {
if (i != min(num + 1, k)) {
ans[i] |= 1LL << bit;
}
}
if (num < k) num++;
} else {
for (int i = 1; i <= num >> 1 << 1; i++) { // 必须是偶数个 1
ans[i] |= 1LL << bit;
}
}
}
for (int i = 1; i <= k; i++) {
cout << ans[i] << ' ';
}
cout << endl;
}
}
D. Insolvable Disks
time limit per test: 2 seconds
memory limit per test: 256 megabytes
You are given n n n different integer points x 1 < x 2 < ... < x n x_1 < x_2 < \ldots < x_n x1<x2<...<xn on the X-axis of the plane. For each 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n, you have to pick a real value r i > 0 r_i > 0 ri>0 and draw a disk with radius r i r_i ri and center x i x_i xi such that no two disks overlap.
You want to choose values r i r_i ri in such a way that the number of tangent pairs of disks is maximized, and you have to find this maximum value.
We say that two disks overlap if the area of their intersection is positive. In particular, two outer tangent disks do not overlap.
笔者的思路是直接从头和从尾开始遍历,处理一个前缀和后缀能填多少个,然后直接枚举分界点,主要是tm题目中限制半径不能是0,于是就设置了一个很小很小的double数字充当半径,然后遍历处理上界和下界,大概这样的思路。
cpp
#define double long double
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1), pre(n + 1), suf(n + 1);
vector<vector<double>> up(2, vector<double>(n + 1)), lo(2, vector<double>(n + 1));
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
if (n == 1) {
cout << 0 << endl;
return;
} else if (n == 2) {
cout << 1 << endl;
return;
}
a.push_back(INF);
lo[0][1] = lo[1][n] = 0.000000001;
up[0][1] = a[2] - a[1], up[1][n] = a[n] - a[n - 1];
for (int i = 2; i <= n; i++) {
double d = a[i] - a[i - 1];
lo[0][i] = d - up[0][i - 1];
// if (lo[0][i] < 1e-12) lo[0][i] = 0.000000001;
up[0][i] = min((double)a[i + 1] - a[i], (double)d - lo[0][i - 1]);
if (lo[0][i] <= up[0][i] && up[0][i] > 0) {
pre[i] = pre[i - 1] + 1;
} else {
lo[0][i] = 0.000000001;
pre[i] = pre[i - 1];
}
}
a[0] = -INF;
for (int i = n - 1; i >= 1; i--) {
int d = a[i + 1] - a[i];
lo[1][i] = d - up[1][i + 1];
// if (lo[1][i] < 1e-12) lo[1][i] = 0.000000001;
up[1][i] = min((double)a[i] - a[i - 1], (double)d - lo[1][i + 1]);
if (lo[1][i] <= up[1][i] && up[1][i] > 0) {
suf[i] = suf[i + 1] + 1;
} else {
lo[1][i] = 0.000000001;
suf[i] = suf[i + 1];
}
}
int ans = max(pre[n], suf[1]);
// debug(ans);
for (int i = 2; i < n; i++) {
double L = max(lo[0][i], lo[1][i]);
double R = min(up[0][i], up[1][i]);
if (L <= R && R > 0) {
ans = max(ans, pre[i - 1] + suf[i + 1] + 1);
}
}
for (int i = 1; i < n; i++) {
ans = max(pre[i] + suf[i + 1], ans);
int d = a[i + 1] - a[i];
if (up[1][i + 1] > 0 && up[0][i] > 0 && up[1][i + 1] + up[0][i] >= d + 1e-12 && lo[1][i + 1] + lo[0][i] <= d - 1e-12) {
ans = max(pre[i] + suf[i + 1] + 1, ans);
}
}
cout << ans << endl;
}
E. No Effect XOR
time limit per test: 2 seconds
memory limit per test: 256 megabytes
In the jungle, there is a lake with infinite lily pads on it. The lily pads are numbered with non-negative integers 0 , 1 , 2 , 3 , ... 0, 1, 2, 3, \ldots 0,1,2,3,.... The lily pads with numbers between l l l and r r r inclusive are called suitable, while all other lily pads are not suitable for the frogs to sit on.
Currently, a single frog is sitting on each suitable lily pad.
Ostad is watching the lake and wants to reorder the frogs. To do so, Ostad can pick a positive integer x x x and announce it to the frogs. After hearing the number, the frog sitting on the i i i-th lily pad will jump to the ( i ⊕ x ) (i \oplus x) (i⊕x)-th one, where ⊕ \oplus ⊕ denotes the bitwise XOR operation.
Ostad likes the frogs, and therefore he wants to pick the number x x x in such a way that all frogs stay within the range of suitable lily pads.
Help Ostad by counting how many different numbers x x x Ostad can choose such that no frog jumps outside the suitable segment of the lily pads.
看了官方题解研究了好一会儿才看懂到底是怎么做的...
首先要有几个对于 X O R XOR XOR 以及位运算的理解:
-
l o w b i t ( x ) lowbit(x) lowbit(x):返回 x x x 二进制表示下,最后一个是1的数字是多少,也可以理解为,最大的能整除x的2的整数次幂数字 。eg: l o w b i t ( 12 ) lowbit(12) lowbit(12) 就返回 4 4 4,因为 4 4 4 是最大的能整除 12 12 12 的 2 2 2 的整数次幂数字。
-
X O R XOR XOR运算具有某个神奇的分块映射性质:例如对于长度 16 的数组,从 0 ~ 16 ; p = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] p = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] p=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
所有元素对 4 4 4 异或的结果: p = [ 4 , 5 , 6 , 7 , 0 , 1 , 2 , 3 , 12 , 13 , 14 , 15 , 8 , 9 , 10 , 11 ] p = [4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11] p=[4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11]
所有元素对 5 5 5 异或的结果: p = [ 5 , 4 , 7 , 6 , 1 , 0 , 3 , 2 , 13 , 12 , 15 , 14 , 9 , 8 , 11 , 10 ] p = [5,4,7,6,1,0,3,2,13,12,15,14,9,8,11,10] p=[5,4,7,6,1,0,3,2,13,12,15,14,9,8,11,10]
可以发现对于 [0, 7] 之间的八个元素依旧在前8个的位置,相当于一个长度为 8 的块(窗口)。
-
同样这里又可以引申出一个基的概念:即,如果我们把所有的元素对 4 异或之后再次对于 5 异或,其实元素依然不会跑出自己所在的八个元素构成的块。所以存在一组基础的数字使得对于任意一些数字异或之后依然不会跑出所在的原有块的范围,这一组最少的数字就是基。(有点像线性基)
综上,我们就对需要找出如何分块,使得 l l l 或者 r r r 都不会把一个块分割成两部分。或者,l,r正好是和中间的部分对称,那么就会多一个基。
大概如上,具体可以参考官方题解
cpp
inline int lowbit(const int &x) { return x & -x; }
inline int check(const int &x) { return x == lowbit(x); }
void solve()
{
int l, r;
cin >> l >> r;
int bit = 1LL << (max(__lg(l), __lg(r)));
while ((bit & l) == (bit & r) && bit > 0) {
if (bit & l) l -= bit, r -= bit;
bit >>= 1LL;
}
if (l == 0) {
cout << lowbit(r + 1) - 1LL << endl;
} else if (check(l + r + 1)) {
cout << 2LL * min(lowbit(r + 1), lowbit(l)) - 1LL << endl;
} else {
cout << min(lowbit(r + 1), lowbit(l)) - 1LL << endl;
}
}