A. Rectangle Cutting
鲍勃有一个大小为 a×ba \times ba×b 的矩形。他尝试将这个矩形切成两个边长为整数的矩形,切口平行于原矩形的一条边。然后,鲍勃试图用这两个矩形拼成另一个矩形,他可以随意旋转和移动这两个矩形。
请注意,如果两个矩形仅有 90∘90^{\circ}90∘ 次旋转的区别,那么它们就被视为相同的矩形。例如,矩形 6×46 \times 46×4 和 4×64 \times 64×6 被认为是相同的。
因此,从 2×62 \times 62×6 矩形可以形成另一个矩形,因为它可以切割成两个 2×32 \times 32×3 矩形,然后用这两个矩形形成与 2×62 \times 62×6 矩形不同的 4×34 \times 34×3 矩形。

但是,从 2×12 \times 12×1 矩形中却不能形成另一个矩形,因为它只能被切割成两个 1×11 \times 11×1 矩形,而从这两个矩形中只能形成 1×21 \times 21×2 和 2×12 \times 12×1 矩形,这两个矩形被认为是相同的。

帮助鲍勃确定他是否能得到其他矩形,或者他是否只是在浪费时间。
输入
每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤1041 \leq t \leq 10^41≤t≤104 ) - 测试用例的数量。随后是测试用例的描述。
每个测试用例的单行包含两个整数 aaa 和 bbb ( 1≤a,b≤1091 \le a, b \le 10^91≤a,b≤109 ) --鲍勃矩形的大小。
输出
对于每个测试用例,如果鲍勃能从 a×ba \times ba×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}† 和找出出现频率最高的元素。最近,他发现了一个数组 aaa ,于是决定找出在数组 aaa 中添加一些排列组合后,数组 aaa 中等于相同数字的元素的最大数目。
更具体地说,瓦夏必须选择一个长度为 nnn 的排列 p1,p2,p3,…,pnp_1, p_2, p_3, \ldots, p_np1,p2,p3,…,pn ,然后根据规则 ai:=ai+pia_i := a_i + p_iai:=ai+pi 改变数组 aaa 中的元素。之后,瓦夏计算每个数字在数组 aaa 中出现的次数,并取其中的最大值。即求得相同元素的最大频率。
†^{\dagger}† 长度为 nnn 的排列是由 nnn 个不同的整数组成的数组,这些整数从 111 到 nnn 按任意顺序排列。例如, [2,3,1,5,4][2,3,1,5,4][2,3,1,5,4] 是一个排列,但 [1,2,2][1,2,2][1,2,2] 不是一个排列( 222 在数组中出现了两次), [1,3,4][1,3,4][1,3,4] 也不是一个排列( n=3n=3n=3 ,但数组中有 444 )。
输入
每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤2⋅1041 \leq t \leq 2 \cdot 10^41≤t≤2⋅104 ) - 测试用例的数量。然后是测试用例的描述。
每个测试用例的第一行包含一个整数 nnn ( 1≤n≤2⋅1051 \le n \le 2 \cdot 10^51≤n≤2⋅105 ) - 数组的长度 aaa 。
每个测试用例的第二行包含 nnn 个整数 a1,a2,…,ana_1, a_2, \ldots, a_na1,a2,…,an ( 1≤ai≤1091 \le a_i \le 10^91≤ai≤109 ) - 数组 aaa 的元素。
保证所有测试用例的 nnn 之和不超过 2⋅1052 \cdot 10^52⋅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
思路
容易知道数组的顺序和重复元素并不会对答案有影响,于是先将数组排序并去重,用双指针维护当前取得相同元素的区间,假设目前遍历到 aia_iai ,相同元素为 ai+1a_i + 1ai+1,则左指针 jjj 必须满足 aj+n≥ai+1a_j+n\ge a_i+1aj+n≥ai+1,如此区间 [j,i][j,i][j,i] 的元素加上排列 ppp 后能取得相同元素的个数为区间长度 i−j+1i - j + 1i−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
这样,每隔 2k−22k - 22k−2 个位置就重复一轮。
男孩瓦夏经常忘记所有事情。例如,他忘记了上面描述的数字 kkk 。但是他记得他在队伍中的位置 nnn,以及他位置的数字 xxx。请帮助瓦夏理解在给定的限制条件下,有多少个自然数 kkk 符合要求,注意 k>1k>1k>1。
输入
每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤1001 \leq t \leq 1001≤t≤100 ) - 测试用例的数量。随后是测试用例的描述。
每个测试用例的唯一一行包含两个整数 nnn 和 xxx ( 1≤x<n≤1091 \le x \lt n \le 10^91≤x<n≤109 )–瓦西亚在该行中的位置和瓦西亚在结算时收到的数字。
输出
对于每个测试用例,输出一个整数 - 在给定约束条件下符合要求的不同 kkk 的数量。
可以证明,在给定的约束条件下,答案是有限的。
样例
5
10 2
3 1
76 4
100 99
1000000000 500000000
4
1
9
0
1
注
在第一个测试案例中, kkk 等于 2,3,5,62, 3, 5, 62,3,5,6 是合适的。
解决这些问题的一个例子是 kkk :
| kkk / № | 111 | 222 | 333 | 444 | 555 | 666 | 777 | 888 | 999 | 101010 |
|---|---|---|---|---|---|---|---|---|---|---|
| 222 | 111 | 222 | 111 | 222 | 111 | 222 | 111 | 222 | 111 | 222 |
| 333 | 111 | 222 | 333 | 222 | 111 | 222 | 333 | 222 | 111 | 222 |
| 555 | 111 | 222 | 333 | 444 | 555 | 444 | 333 | 222 | 111 | 222 |
| 666 | 111 | 222 | 333 | 444 | 555 | 666 | 555 | 444 | 333 | 222 |
在第二个测试用例中, k=2k = 2k=2 是合适的。
思路
与题目规定不一样,不妨令 k=mx−1k=mx-1k=mx−1,mxmxmx 是序列的最大值,那么每 2k2k2k 个数就是一次循环,观察样例可知,xxx 出现的位置满足如下公式:
n=x+2k∗c (c>0),n=2k∗(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<nx < nx<n,所以 ccc 不能为0,即 xxx 不能出现在第一次的上升段
第二条公式代表序列的下降段,由于 xxx 可以出现在第一次下降段,所以 ccc 可以为0
第三条公式为题目要求
第四条公式显然,xxx 需要在 [1,mx][1,mx][1,mx] 范围内
第五条公式因为 xxx 不能出现在第一次的上升段,所以必须满足 mx<nmx < nmx<n
接下来暴力枚举 ccc 求得 kkk 即可,复杂度是根号级别,将 kkk 用 setsetset 去重并统计范围内的可行解。
#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
给你 nnn 个种类的小球,第 iii 种小球的数量等于 cic_ici,你可以将小球分为 kkk 组存放,对于同一种小球来说,两个小球不在同一组贡献 +b+b+b ,在同一组则贡献为0 。组数 kkk 贡献为 −(k−1)⋅x-(k-1)\cdot x−(k−1)⋅x,注意至少有一个组,请给出分配小球的最大贡献。
输入
每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤2⋅1041 \le t \le 2 \cdot 10^41≤t≤2⋅104 ) - 测试用例的个数。测试用例说明如下。
每个测试用例的第一行包含三个整数 nnn 、 bbb 和 xxx ( 1≤n≤2⋅1051 \le n \le 2 \cdot 10^51≤n≤2⋅105 、 1≤b≤106,0≤x≤1091 \le b \le 10^6, 0 \le x \le 10^91≤b≤106,0≤x≤109 ) 。
每个测试用例的第二行包含 nnn 个整数 c1,c2,…,cnc_1, c_2, \ldots, c_nc1,c2,…,cn (1≤ci≤2⋅1051 \le c_i \le 2 \cdot 10^51≤ci≤2⋅105) 。
保证所有测试用例的数值 c1+c2+…+cnc_1 + c_2 + \ldots + c_nc1+c2+…+cn 之和不超过 2⋅1052 \cdot 10^52⋅105 。
输出
对于每个测试用例,输出最大贡献。
思路
容易想到贪心,对于每个种类小球,分组策略为尽量平均,假设分为 ppp 组,则第 iii 种小球在组中要么是 ⌊ci/p⌋\left \lfloor c_i/p \right \rfloor⌊ci/p⌋,要么是⌈ci/p⌉\left \lceil c_i/p \right \rceil⌈ci/p⌉,如果 x=0x=0x=0,显然组数越多越好,答案单调递增,x≠0x\ne0x=0 时,随着组数 kkk 增大,答案上升到一定值后单调递减,为上抛物线函数,三分搜索即可。
#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;
}
1050

被折叠的 条评论
为什么被折叠?



