A - Rectangle Cutting
链接:A - Rectangle Cutting
思路
考虑横边和纵边,若为偶数,则从中间分开,重新组合为一个长方形,检测是否与原来的长方形一致。
代码
#include <bits/stdc++.h>
using namespace std;
inline bool test(int x, int y, int a, int b){
if(x == a && y == b) return false;
else if(x == b && y == a) return false;
return true;
}
inline bool solve(int x, int y){
if(x & 1) return false;
return test(x, y, x / 2, y * 2);
}
int main()
{
int T;
cin >> T;
while(T--){
int a, b;
scanf("%d%d", &a, &b);
if(solve(a, b) || solve(b, a)){
puts("YES");
}
else{
puts("NO");
}
}
return 0;
}
B - Equalize
链接:B - Equalize
思路
我们发现原本相同的元素在经过操作之后必定会变得不相同,即最终相同的元素在操作之前并不相同,所以我们可以执行去重操作。
由于分配全排列类似于阶梯,所以其可以把值为 [ k , k + n ) [k, k + n) [k,k+n) 范围内的元素进行操作,使得最终值相同。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 20;
int n;
int a[N];
int main()
{
int T;
cin >> T;
while(T--)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", a + i);
sort(a + 1, a + 1 + n);
int len = unique(a + 1, a + 1 + n) - (a + 1);
int l = 1;
int ans = 1;
for(int r = 1; r <= len; r ++){
while(a[r] - a[l] >= n){
l ++;
}
ans = max(ans, r - l + 1);
}
printf("%d\n", ans);
}
return 0;
}
C - Physical Education Lesson
链接:C - Physical Education Lesson
题意
总共有 n n n 个人,有个参数 k k k ,从前往后依次给这些人分配序号,序号的分配规则如下:
- 前 k 个人的号码是 1 , 2 , 3 , … , k 1,2,3,…,k 1,2,3,…,k
- 接下来 k − 2 k-2 k−2 个人的号码为 k − 1 , k − 2 , … , 2 k−1, k−2, …, 2 k−1,k−2,…,2
- 然后以 2 k − 2 2k - 2 2k−2 为周期,依次给剩下位置的人分配指标
现在知道一个人的位置和这个人所得到的指标,k 的可能取值范围有多少个?
思路
分为一下两种情况:
由于周期为 2 k − 2 2k - 2 2k−2 ,所以统统把位置通过取模运算转化到前 2 k − 2 2k - 2 2k−2 个等价的位置。
①这个人得到的指标位于正序的情况中(1,2,3,…,k)【void yinsu1(int x)】
转化位置:
(
n
−
1
)
%
(
2
k
−
2
)
+
1
=
x
(n - 1)\%(2k-2)+1=x
(n−1)%(2k−2)+1=x
即:
(
n
−
1
)
%
(
2
k
−
2
)
=
x
−
1
(n - 1)\%(2k-2)=x-1
(n−1)%(2k−2)=x−1
而
x
≤
k
x \le k
x≤k ,则有得
x
−
1
<
2
k
−
2
x-1 < 2k-2
x−1<2k−2 ,所以有同余关系:
n
−
1
=
x
−
1
m
o
d
(
2
k
−
2
)
n
−
x
=
0
m
o
d
(
2
k
−
2
)
n - 1=x-1\mod (2k-2) \\ n - x= 0 \mod (2k-2) \\
n−1=x−1mod(2k−2)n−x=0mod(2k−2)
当
(
n
−
x
)
(n-x)
(n−x) 可以整除
2
(
k
−
1
)
2(k - 1)
2(k−1) 的时候,那么就成立。
所以应该求 ( n − x ) 2 \frac{(n-x)}{2} 2(n−x)的所有因数,k即为 因数 + 1 因数+1 因数+1
时间复杂度为 O ( n ) O(\sqrt{n}) O(n)
②这个人得到的指标位于倒序的情况中(k−1, k−2, …, 2)【void yinsu2】
在这种情况下, x x x 自然就不可能为 k k k 或者是 1 1 1
与上面类似,可以得出等式:
(
n
−
1
)
%
(
2
k
−
2
)
+
1
=
2
k
−
x
(n - 1)\% (2k-2)+1=2k -x
(n−1)%(2k−2)+1=2k−x
即:
(
n
−
1
)
%
(
2
k
−
2
)
=
2
k
−
x
−
1
(n - 1)\% (2k-2)=2k -x-1
(n−1)%(2k−2)=2k−x−1
由于 x 不可能为 1,且为自然数,那么有
2
k
−
2
<
2
k
−
1
−
x
2k-2 < 2k-1-x
2k−2<2k−1−x
故有:
n
−
1
=
2
k
−
x
−
1
m
o
d
(
2
k
−
2
)
n
+
x
−
2
k
=
0
m
o
d
2
(
k
−
1
)
n - 1=2k -x-1 \mod (2k-2) \\ n +x-2k=0 \mod 2(k-1) \\
n−1=2k−x−1mod(2k−2)n+x−2k=0mod2(k−1)
所以需要求使得
n
+
x
−
2
k
n+x-2k
n+x−2k 可以被
2
(
k
−
1
)
2(k - 1)
2(k−1) 整除的 k 的个数
设做除法之后的商为 t,则有:
n
+
x
−
2
k
2
(
k
−
1
)
=
t
n
+
x
2
−
k
(
k
−
1
)
=
t
n
+
x
2
−
k
=
k
t
−
t
k
(
1
+
t
)
=
n
+
x
2
+
t
k
=
n
+
x
2
+
t
1
+
t
k
=
n
+
x
2
−
1
+
(
t
+
1
)
t
+
1
\frac{n+x-2k}{2(k-1)}=t\\ \frac{\frac{n+x}{2}-k}{(k-1)}=t\\ \frac{n+x}{2}-k=kt-t\\ k(1+t)=\frac{n+x}{2}+t\\ k = \frac{\frac{n+x}{2}+t}{1+t}\\ k = \frac{\frac{n+x}{2}-1+(t+1)}{t+1}\\
2(k−1)n+x−2k=t(k−1)2n+x−k=t2n+x−k=kt−tk(1+t)=2n+x+tk=1+t2n+x+tk=t+12n+x−1+(t+1)
此时,仅仅需要找
n
+
x
2
−
1
\frac{n+x}{2}-1
2n+x−1 的因数,然后有:
k
=
n
+
x
2
−
1
因数
+
1
k=\frac{ \frac{n+x}{2}-1 }{因数}+1
k=因数2n+x−1+1
由于有
x
≤
k
x \le k
x≤k,并且 k 可能会重复,所以进行去重,并且仅仅考虑大于等于 x 的 k
代码
#include <bits/stdc++.h>
using namespace std;
vector<int> ans;
int n, x;
void yinsu1(int x){// 考虑了1这一个因数, 正常求得所有的因数,然后放在数组中
for(int i = 1; (long long)i * i <= (long long)x; i++){
if(x % i == 0){
if((long long)i * i == (long long)x) ans.push_back(i + 1);
else {
ans.push_back(i + 1);
ans.push_back(x / i + 1);
}
}
}
return;
}
void yinsu2(int x){
for(int i = 1; (long long)i * i <= (long long)x; i++){
if(x % i == 0){
if((long long)i * i == (long long)x) ans.push_back(x / i + 1);
else {
ans.push_back(x / i + 1);
int tmp = (x / i);
ans.push_back(x / tmp + 1);
}
}
}
}
int main()
{
int T;
cin >> T;
while(T--){
ans.clear();
scanf("%d%d", &n, &x);
if(x > n) {// 不可能
puts("0");
continue;
}
// 计算在正序范围内的
if(!((n - x) & 1))
yinsu1(abs(n - x) / 2);
if(x != 1 && x != n && !((n + x) & 1))// 倒序中不含1和n
yinsu2(abs(n + x) / 2 - 1);
sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());
int cnt = 0;
for(int _ : ans){
if(_ >= max(2, x)) cnt++;
}
printf("%d\n", cnt);
}
return 0;
}
D - Lonely Mountain Dungeons
链接:D - Lonely Mountain Dungeons
题意
有n个不同的种群,每一个种群中的生物数量为c[i].
假设现在有 t 个分队,那么计算总兵力的方法如下:
- 针对每一种种群,当有一对生物处于两个小队,那么战斗力会增加 b
- 减去 x ( t − 1 ) x(t-1) x(t−1)
思路
最重要的是有一个温馨提示:
保证所有测试用例的数值 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
这样使得我们可以进行暴力。
①单独对于某一种群,查看如何分配,才可以使得这一种种群的战斗力最大
我们可以采用移动法:
把某一小队的一个生物移动到其他分队,查看其总贡献的变化。
经过推敲,我们容易知道,当把一个生物从A队移动到B队,当且仅当A队的生物的数量个数(不包含移动的那一个)多余B队生物数量(不包含移动的那一个)时,才会更加有意义。
所以合理的排布为:
- 先给每一小队分配 ⌊ c [ i ] t ⌋ \lfloor \frac{c[i]}{t} \rfloor ⌊tc[i]⌋。
- 然后把剩余的给每一小队分一个,直到分完。
②如何计算某一种群按照①的方法分配之后的贡献值
根据数学知识,知道:
C
k
−
c
m
o
d
k
2
⋅
y
2
+
C
c
m
o
d
k
2
⋅
y
′
2
+
(
k
−
c
m
o
d
k
)
⋅
(
c
m
o
d
k
)
⋅
y
⋅
y
′
C_{k - c \bmod k}^2 \cdot y^2 + C_{c \bmod k}^2 \cdot y'^2 + (k - c \bmod k) \cdot (c \bmod k) \cdot y \cdot y'
Ck−cmodk2⋅y2+Ccmodk2⋅y′2+(k−cmodk)⋅(cmodk)⋅y⋅y′
其中
y
=
⌊
c
k
⌋
y = \left\lfloor \frac{c}{k} \right\rfloor
y=⌊kc⌋ ,
y
′
=
⌈
c
k
⌉
y' = \left\lceil \frac{c}{k} \right\rceil
y′=⌈kc⌉.
③时间复杂度问题
当 $t > c[i] $ 的时候,我们发现这个种群的贡献最多为 t = = c [ i ] t==c[i] t==c[i] 时候的情况,这个时候,我们就没有必要计算 $t > c[i] $ 的时候这个种群的贡献,仅仅需要通过add数组记录即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, b, x;
const int N = 2e5 + 20;
int a[N];
int cnt[N];
int add[N];
int getPair(int n, int k){
int x = n / k;
int y = n % k;
return (x+1) * (x+1) * (y*(y-1))/2
+ x * x * (k-y)*(k-y-1)/2
+ (x+1) * x * (y) * (k - y);
}
void solve(){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= a[i]; j++){
cnt[j] += getPair(a[i], j);
}
add[a[i]] += getPair(a[i], a[i]);
}
}
void init(int maxv){
for(int i = 1; i <= maxv + 1; i++){
add[i] = 0;
cnt[i] = 0;
}
}
signed main()
{
int T;
cin >> T;
while(T--){
scanf("%lld%lld%lld", &n, &b, &x);
for(int i = 1; i <= n; i++) scanf("%lld", a + i);
int maxv = 0;
for(int i = 1; i <= n; i++){
maxv = max(maxv, a[i]);
}
init(maxv);
solve();
int ans = 0;
int other = 0;
for(int i = 1; i <= maxv; i++){
ans = max(ans, (cnt[i] + other) * b - (i - 1) * x);
other += add[i];
}
printf("%lld\n", ans);
}
return 0;
}