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

A. Rectangle Cutting

鲍勃有一个大小为 a×ba \times ba×b 的矩形。他尝试将这个矩形切成两个边长为整数的矩形,切口平行于原矩形的一条边。然后,鲍勃试图用这两个矩形拼成另一个矩形,他可以随意旋转和移动这两个矩形。

请注意,如果两个矩形仅有 90∘90^{\circ}90 次旋转的区别,那么它们就被视为相同的矩形。例如,矩形 6×46 \times 46×44×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×22×12 \times 12×1 矩形,这两个矩形被认为是相同的。

帮助鲍勃确定他是否能得到其他矩形,或者他是否只是在浪费时间。

输入

每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤1041 \leq t \leq 10^41t104 ) - 测试用例的数量。随后是测试用例的描述。

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

输出

对于每个测试用例,如果鲍勃能从 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 个不同的整数组成的数组,这些整数从 111nnn 按任意顺序排列。例如, [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^41t2104 ) - 测试用例的数量。然后是测试用例的描述。

每个测试用例的第一行包含一个整数 nnn ( 1≤n≤2⋅1051 \le n \le 2 \cdot 10^51n2105 ) - 数组的长度 aaa

每个测试用例的第二行包含 nnn 个整数 a1,a2,…,ana_1, a_2, \ldots, a_na1,a2,,an ( 1≤ai≤1091 \le a_i \le 10^91ai109 ) - 数组 aaa 的元素。

保证所有测试用例的 nnn 之和不超过 2⋅1052 \cdot 10^52105

输出

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

样例

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

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

男孩瓦夏经常忘记所有事情。例如,他忘记了上面描述的数字 kkk 。但是他记得他在队伍中的位置 nnn,以及他位置的数字 xxx。请帮助瓦夏理解在给定的限制条件下,有多少个自然数 kkk 符合要求,注意 k>1k>1k>1

输入

每个测试由多个测试用例组成。第一行包含一个整数 ttt ( 1≤t≤1001 \leq t \leq 1001t100 ) - 测试用例的数量。随后是测试用例的描述。

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

输出

对于每个测试用例,输出一个整数 - 在给定约束条件下符合要求的不同 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 / №111222333444555666777888999101010
222111222111222111222111222111222
333111222333222111222333222111222
555111222333444555444333222111222
666111222333444555666555444333222

在第二个测试用例中, k=2k = 2k=2 是合适的。

思路

与题目规定不一样,不妨令 k=mx−1k=mx-1k=mx1mxmxmx 是序列的最大值,那么每 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+2kc (c>0),n=2k(c+1)+2x (c0),k1,1xk+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 即可,复杂度是根号级别,将 kkksetsetset 去重并统计范围内的可行解。

#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(k1)x,注意至少有一个组,请给出分配小球的最大贡献。

输入

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

每个测试用例的第一行包含三个整数 nnnbbbxxx ( 1≤n≤2⋅1051 \le n \le 2 \cdot 10^51n21051≤b≤106,0≤x≤1091 \le b \le 10^6, 0 \le x \le 10^91b106,0x109 ) 。

每个测试用例的第二行包含 nnn 个整数 c1,c2,…,cnc_1, c_2, \ldots, c_nc1,c2,,cn (1≤ci≤2⋅1051 \le c_i \le 2 \cdot 10^51ci2105) 。

保证所有测试用例的数值 c1+c2+…+cnc_1 + c_2 + \ldots + c_nc1+c2++cn 之和不超过 2⋅1052 \cdot 10^52105

输出

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

思路

容易想到贪心,对于每个种类小球,分组策略为尽量平均,假设分为 ppp 组,则第 iii 种小球在组中要么是 ⌊ci/p⌋\left \lfloor c_i/p \right \rfloorci/p,要么是⌈ci/p⌉\left \lceil c_i/p \right \rceilci/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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值