A. Rectangle Cutting
鲍勃有一个大小为 a × b a \times b a×b 的矩形。他尝试将这个矩形切成两个边长为整数的矩形,切口平行于原矩形的一条边。然后,鲍勃试图用这两个矩形拼成另一个矩形,他可以随意旋转和移动这两个矩形。
请注意,如果两个矩形仅有 9 0 ∘ 90^{\circ} 90∘ 次旋转的区别,那么它们就被视为相同的矩形。例如,矩形 6 × 4 6 \times 4 6×4 和 4 × 6 4 \times 6 4×6 被认为是相同的。
因此,从 2 × 6 2 \times 6 2×6 矩形可以形成另一个矩形,因为它可以切割成两个 2 × 3 2 \times 3 2×3 矩形,然后用这两个矩形形成与 2 × 6 2 \times 6 2×6 矩形不同的 4 × 3 4 \times 3 4×3 矩形。
但是,从 2 × 1 2 \times 1 2×1 矩形中却不能形成另一个矩形,因为它只能被切割成两个 1 × 1 1 \times 1 1×1 矩形,而从这两个矩形中只能形成 1 × 2 1 \times 2 1×2 和 2 × 1 2 \times 1 2×1 矩形,这两个矩形被认为是相同的。
帮助鲍勃确定他是否能得到其他矩形,或者他是否只是在浪费时间。
输入
每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1≤t≤104 ) - 测试用例的数量。随后是测试用例的描述。
每个测试用例的单行包含两个整数 a a a 和 b b b ( 1 ≤ a , b ≤ 1 0 9 1 \le a, b \le 10^9 1≤a,b≤109 ) --鲍勃矩形的大小。
输出
对于每个测试用例,如果鲍勃能从 a × b a \times b a×b 矩形中获得另一个矩形,则输出 “是”。否则,输出 “否”。
可以用任何大小写(大写或小写)输出答案。例如,字符串 “yEs”、“yes”、"Yes "和 "YES "将被识别为肯定答案。
样例
7
1 1
2 1
2 6
3 2
2 2
2 4
6 3
No
No
Yes
Yes
Yes
Yes
No
思路
从样例就可以看出来,只能从纵向中间切一刀或者横向中间切一刀拼成新的矩形,判断一下新矩形是否和原本的相同即可。
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<functional>
#include<stack>
#include<list>
#include<iomanip>
#include<array>
#include<cassert>
#define fi first
#define se second
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { { -1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int N = 5e5 + 5, M = 1e6 + 5;
const ll inf = (1ll << 50);
int mod = 1e9 + 7;
int main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int t;
cin >> t;
while(t--) {
int a, b;
cin >> a >> b;
auto check = [&](int x, int y) {
int tx = x, ty = y;
if(y & 1) return false;
y /= 2, x *= 2;
if(y == tx && x == ty) return false;
return true;
};
if(check(a, b) || check(b, a)) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
B. Equalize
瓦夏有两个爱好——给数组添加排列 † ^{\dagger} † 和找出出现频率最高的元素。最近,他发现了一个数组 a a a ,于是决定找出在数组 a a a 中添加一些排列组合后,数组 a a a 中等于相同数字的元素的最大数目。
更具体地说,瓦夏必须选择一个长度为 n n n 的排列 p 1 , p 2 , p 3 , … , p n p_1, p_2, p_3, \ldots, p_n p1,p2,p3,…,pn ,然后根据规则 a i : = a i + p i a_i := a_i + p_i ai:=ai+pi 改变数组 a a a 中的元素。之后,瓦夏计算每个数字在数组 a a a 中出现的次数,并取其中的最大值。即求得相同元素的最大频率。
† ^{\dagger} † 长度为 n n n 的排列是由 n n n 个不同的整数组成的数组,这些整数从 1 1 1 到 n n n 按任意顺序排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4] 是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2] 不是一个排列( 2 2 2 在数组中出现了两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4] 也不是一个排列( n = 3 n=3 n=3 ,但数组中有 4 4 4 )。
输入
每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 2 ⋅ 1 0 4 1 \leq t \leq 2 \cdot 10^4 1≤t≤2⋅104 ) - 测试用例的数量。然后是测试用例的描述。
每个测试用例的第一行包含一个整数 n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1≤n≤2⋅105 ) - 数组的长度 a a a 。
每个测试用例的第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an ( 1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1≤ai≤109 ) - 数组 a a a 的元素。
保证所有测试用例的 n n n 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2⋅105 。
输出
对于每个测试用例,输出一个数字 - 在添加排列组合操作后,等于相同数字的元素的最大数量。
样例
7
2
1 2
4
7 1 4 1
3
103 102 104
5
1 101 1 100 1
5
1 10 100 1000 1
2
3 1
3
1000000000 999999997 999999999
2
2
3
2
1
1
2
思路
容易知道数组的顺序和重复元素并不会对答案有影响,于是先将数组排序并去重,用双指针维护当前取得相同元素的区间,假设目前遍历到 a i a_i ai ,相同元素为 a i + 1 a_i + 1 ai+1,则左指针 j j j 必须满足 a j + n ≥ a i + 1 a_j+n\ge a_i+1 aj+n≥ai+1,如此区间 [ j , i ] [j,i] [j,i] 的元素加上排列 p p p 后能取得相同元素的个数为区间长度 i − j + 1 i - j + 1 i−j+1。
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<functional>
#include<stack>
#include<list>
#include<iomanip>
#include<array>
#include<cassert>
#define fi first
#define se second
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { { -1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int N = 5e5 + 5, M = 1e6 + 5;
const ll inf = (1ll << 50);
int mod = 1e9 + 7;
int main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
int j = 0;
int ans = 0;
for(int i = 0;i < a.size();i++) {
while(a[j] + n < a[i] + 1) ++j;
ans = max(ans, i - j + 1);
}
cout << ans << "\n";
}
return 0;
}
C. Physical Education Lesson
在一所著名的学校里,有一堂体育课。像往常一样,每个人都排成一排,排队方式如下,像波浪一样起伏:
1
,
2
,
3...
,
k
,
k
−
1
,
k
−
2
,
.
.
.
,
1
,
2
,
3
1,2,3...,k,k-1,k-2,...,1,2,3
1,2,3...,k,k−1,k−2,...,1,2,3
这样,每隔 2 k − 2 2k - 2 2k−2 个位置就重复一轮。
男孩瓦夏经常忘记所有事情。例如,他忘记了上面描述的数字 k k k 。但是他记得他在队伍中的位置 n n n,以及他位置的数字 x x x。请帮助瓦夏理解在给定的限制条件下,有多少个自然数 k k k 符合要求,注意 k > 1 k>1 k>1。
输入
每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 100 1 \leq t \leq 100 1≤t≤100 ) - 测试用例的数量。随后是测试用例的描述。
每个测试用例的唯一一行包含两个整数 n n n 和 x x x ( 1 ≤ x < n ≤ 1 0 9 1 \le x \lt n \le 10^9 1≤x<n≤109 )–瓦西亚在该行中的位置和瓦西亚在结算时收到的数字。
输出
对于每个测试用例,输出一个整数 - 在给定约束条件下符合要求的不同 k k k 的数量。
可以证明,在给定的约束条件下,答案是有限的。
样例
5
10 2
3 1
76 4
100 99
1000000000 500000000
4
1
9
0
1
注
在第一个测试案例中, k k k 等于 2 , 3 , 5 , 6 2, 3, 5, 6 2,3,5,6 是合适的。
解决这些问题的一个例子是 k k k :
k k k / № | 1 1 1 | 2 2 2 | 3 3 3 | 4 4 4 | 5 5 5 | 6 6 6 | 7 7 7 | 8 8 8 | 9 9 9 | 10 10 10 |
---|---|---|---|---|---|---|---|---|---|---|
2 2 2 | 1 1 1 | 2 2 2 | 1 1 1 | 2 2 2 | 1 1 1 | 2 2 2 | 1 1 1 | 2 2 2 | 1 1 1 | 2 2 2 |
3 3 3 | 1 1 1 | 2 2 2 | 3 3 3 | 2 2 2 | 1 1 1 | 2 2 2 | 3 3 3 | 2 2 2 | 1 1 1 | 2 2 2 |
5 5 5 | 1 1 1 | 2 2 2 | 3 3 3 | 4 4 4 | 5 5 5 | 4 4 4 | 3 3 3 | 2 2 2 | 1 1 1 | 2 2 2 |
6 6 6 | 1 1 1 | 2 2 2 | 3 3 3 | 4 4 4 | 5 5 5 | 6 6 6 | 5 5 5 | 4 4 4 | 3 3 3 | 2 2 2 |
在第二个测试用例中, k = 2 k = 2 k=2 是合适的。
思路
与题目规定不一样,不妨令
k
=
m
x
−
1
k=mx-1
k=mx−1,
m
x
mx
mx 是序列的最大值,那么每
2
k
2k
2k 个数就是一次循环,观察样例可知,
x
x
x 出现的位置满足如下公式:
n
=
x
+
2
k
∗
c
(
c
>
0
)
,
n
=
2
k
∗
(
c
+
1
)
+
2
−
x
(
c
≥
0
)
,
k
≥
1
,
1
≤
x
≤
k
+
1
,
k
+
1
<
n
n=x+2k*c\ (c\gt0), \\ n=2k*(c+1)+2-x\ (c\ge0), \\ k\ge 1, \\ 1\le x\le k+1, \\ k+1<n
n=x+2k∗c (c>0),n=2k∗(c+1)+2−x (c≥0),k≥1,1≤x≤k+1,k+1<n
第一条公式代表序列的上升段,由于题目规定
x
<
n
x < n
x<n,所以
c
c
c 不能为0,即
x
x
x 不能出现在第一次的上升段
第二条公式代表序列的下降段,由于 x x x 可以出现在第一次下降段,所以 c c c 可以为0
第三条公式为题目要求
第四条公式显然, x x x 需要在 [ 1 , m x ] [1,mx] [1,mx] 范围内
第五条公式因为 x x x 不能出现在第一次的上升段,所以必须满足 m x < n mx < n mx<n
接下来暴力枚举 c c c 求得 k k k 即可,复杂度是根号级别,将 k k k 用 s e t set set 去重并统计范围内的可行解。
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<functional>
#include<stack>
#include<list>
#include<iomanip>
#include<array>
#include<cassert>
#define fi first
#define se second
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { { -1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int N = 5e5 + 5, M = 1e6 + 5;
const ll inf = (1ll << 50);
int mod = 1e9 + 7;
set<int> st;
void calc(ll x) {
int res = 0;
for (int i = 1; 1ll * i * i <= x; i++) {
if (x % i == 0) {
st.insert(x / i);
st.insert(i);
}
}
}
int main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int t;
cin >> t;
while (t--) {
st.clear();
int n, x;
cin >> n >> x;
if ((n + x - 2) % 2 == 0) calc((n + x - 2) / 2);
if ((n - x) % 2 == 0) calc((n - x) / 2);
int res = 0;
for(int num : st) {
if(num + 1 >= x && num + 1 < n) ++res;
}
cout << res << "\n";
}
return 0;
}
D. Lonely Mountain Dungeons
给你 n n n 个种类的小球,第 i i i 种小球的数量等于 c i c_i ci,你可以将小球分为 k k k 组存放,对于同一种小球来说,两个小球不在同一组贡献 + b +b +b ,在同一组则贡献为0 。组数 k k k 贡献为 − ( k − 1 ) ⋅ x -(k-1)\cdot x −(k−1)⋅x,注意至少有一个组,请给出分配小球的最大贡献。
输入
每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 2 ⋅ 1 0 4 1 \le t \le 2 \cdot 10^4 1≤t≤2⋅104 ) - 测试用例的个数。测试用例说明如下。
每个测试用例的第一行包含三个整数 n n n 、 b b b 和 x x x ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1≤n≤2⋅105 、 1 ≤ b ≤ 1 0 6 , 0 ≤ x ≤ 1 0 9 1 \le b \le 10^6, 0 \le x \le 10^9 1≤b≤106,0≤x≤109 ) 。
每个测试用例的第二行包含 n n n 个整数 c 1 , c 2 , … , c n c_1, c_2, \ldots, c_n c1,c2,…,cn ( 1 ≤ c i ≤ 2 ⋅ 1 0 5 1 \le c_i \le 2 \cdot 10^5 1≤ci≤2⋅105) 。
保证所有测试用例的数值 c 1 + c 2 + … + c n c_1 + c_2 + \ldots + c_n c1+c2+…+cn 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2⋅105 。
输出
对于每个测试用例,输出最大贡献。
思路
容易想到贪心,对于每个种类小球,分组策略为尽量平均,假设分为 p p p 组,则第 i i i 种小球在组中要么是 ⌊ c i / p ⌋ \left \lfloor c_i/p \right \rfloor ⌊ci/p⌋,要么是 ⌈ c i / p ⌉ \left \lceil c_i/p \right \rceil ⌈ci/p⌉,如果 x = 0 x=0 x=0,显然组数越多越好,答案单调递增, x ≠ 0 x\ne0 x=0 时,随着组数 k k k 增大,答案上升到一定值后单调递减,为上抛物线函数,三分搜索即可。
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<functional>
#include<stack>
#include<list>
#include<iomanip>
#include<array>
#include<cassert>
#define fi first
#define se second
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
const int dir[4][2] = { { -1, 0}, {1, 0}, {0, -1}, {0, 1} };
const int N = 5e5 + 5, M = 1e6 + 5;
const ll inf = (1ll << 50);
int mod = 1e9 + 7;
int a[N], n, b, x;
ll calc(ll p) {
ll res = 0;
for(int i = 1;i <= n;i++) {
int g = a[i] / p, r = a[i] % p;
res += 1ll * (g + 1) * r * (a[i] - g - 1);
res += 1ll * g * (p - r) * (a[i] - g);
}
return res * b / 2 - (p - 1) * x;
}
ll work() {
ll left = 1, right = 1e6;
while (left + 10 < right) {
ll mdl = (2 * left + right) / 3;
ll mdr = (left + 2 * right) / 3;
ll v1 = calc(mdl), v2 = calc(mdr);
if (v1 < v2) left = mdl;
else right = mdr;
}
ll ans = -1e18;
for (ll k = left; k <= right; k++) {
ans = max(ans, calc(k));
}
return ans;
}
int main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0);
int t;
cin >> t;
while(t--) {
cin >> n >> b >> x;
for(int i = 1;i <= n;i++) {
cin >> a[i];
}
cout << work() << "\n";
}
return 0;
}