【做题笔记 Div2 A-D】Codeforces Round 924 (Div. 2)

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 1t104 ) - 测试用例的数量。随后是测试用例的描述。

每个测试用例的单行包含两个整数 a a a b b b ( 1 ≤ a , b ≤ 1 0 9 1 \le a, b \le 10^9 1a,b109 ) --鲍勃矩形的大小。

输出

对于每个测试用例,如果鲍勃能从 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 1t2104 ) - 测试用例的数量。然后是测试用例的描述。

每个测试用例的第一行包含一个整数 n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105 ) - 数组的长度 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 1ai109 ) - 数组 a a a 的元素。

保证所有测试用例的 n n n 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出

对于每个测试用例,输出一个数字 - 在添加排列组合操作后,等于相同数字的元素的最大数量。

样例

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+nai+1,如此区间 [ j , i ] [j,i] [j,i] 的元素加上排列 p p p 后能取得相同元素的个数为区间长度 i − j + 1 i - j + 1 ij+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,k1,k2,...,1,2,3

这样,每隔 2 k − 2 2k - 2 2k2 个位置就重复一轮。

男孩瓦夏经常忘记所有事情。例如,他忘记了上面描述的数字 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 1t100 ) - 测试用例的数量。随后是测试用例的描述。

每个测试用例的唯一一行包含两个整数 n n n x x x 1 ≤ x < n ≤ 1 0 9 1 \le x \lt n \le 10^9 1x<n109 )–瓦西亚在该行中的位置和瓦西亚在结算时收到的数字。

输出

对于每个测试用例,输出一个整数 - 在给定约束条件下符合要求的不同 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=mx1 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+2kc (c>0),n=2k(c+1)+2x (c0),k1,1xk+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 (k1)x,注意至少有一个组,请给出分配小球的最大贡献。

输入

每个测试由多个测试用例组成。第一行包含一个整数 t t t ( 1 ≤ t ≤ 2 ⋅ 1 0 4 1 \le t \le 2 \cdot 10^4 1t2104 ) - 测试用例的个数。测试用例说明如下。

每个测试用例的第一行包含三个整数 n n n b b b x x x ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105 1 ≤ b ≤ 1 0 6 , 0 ≤ x ≤ 1 0 9 1 \le b \le 10^6, 0 \le x \le 10^9 1b106,0x109 ) 。

每个测试用例的第二行包含 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 1ci2105) 。

保证所有测试用例的数值 c 1 + c 2 + … + c n c_1 + c_2 + \ldots + c_n c1+c2++cn 之和不超过 2 ⋅ 1 0 5 2 \cdot 10^5 2105

输出

对于每个测试用例,输出最大贡献。

思路

容易想到贪心,对于每个种类小球,分组策略为尽量平均,假设分为 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值