Codeforces Round #701 (Div. 2)赛后补题报告(A~D)
A. Add and Divide
原题信息
http://codeforces.com/contest/1485/problem/A
解题思路
对于题目基本有两种方式,一种是直接暴力求解,第二种是使用函数求导进行严格证明
暴力求解
a
=
1
e
9
a=1e^9
a=1e9不难看出,操作最多为 50次,因为
2
4
9
=
562949953421312
2 ^ 49 = 562949953421312
249=562949953421312 直接就超了
那么我们的
b
b
b最多也就是加50次,然后进行向下整除的模拟即可
一定要注意
b
=
0
b = 0
b=0这种情况。
函数求导
首先我们设操作次数为
y
y
y,进行
b
+
=
1
b += 1
b+=1的次数为
x
x
x,可以得到的关系式为
y
=
x
+
(
l
o
g
b
+
x
a
+
1
)
y = x + (log_{b+x}a+1)
y=x+(logb+xa+1),其中
l
o
g
b
+
x
a
log_{b+x}a
logb+xa是整除到1的次数,最后 +1是让其变为0
下面我们对他进行求导
y
′
=
1
−
l
n
a
(
l
n
t
)
2
⋅
t
y'=1-\frac{ln{a}}{(lnt)^2\cdot t}
y′=1−(lnt)2⋅tlna,其中
t
=
b
+
x
,
t
≥
2
并
且
t
≥
b
t=b+x,t\geq2 并且 t\geq b
t=b+x,t≥2并且t≥b
查看导函数的零点
(
l
n
t
)
2
⋅
t
=
l
n
a
,
t
≥
2
并
且
t
≥
b
{(lnt)^2\cdot t}=lna,t\geq2 并且 t\geq b
(lnt)2⋅t=lna,t≥2并且t≥b,等式左侧递增,右侧为常数
l
n
a
lna
lna,即至多有一个零点
倘若有一个零点
可以直接二分求得零点,我们在零点,零点+1, 零点-1进行求解得结果。
倘若没有零点
直接就是
t
≥
2
并
且
t
≥
b
t\geq2 并且 t\geq b
t≥2并且t≥b取得最小值。
AC代码
首先必须吐嘲一下,暴力没往这个向限制,求导生疏了,整了半天,真鸡儿狗
暴力枚举代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
LL a, b;
int sol(LL a, LL b, int add)
{
b += add;
if (b == 1) return 0x3f3f3f3f;
while (a)
{
a /= b;
add ++;
}
return add;
}
int main()
{
int T; cin >> T;
while (T -- )
{
scanf("%lld%lld", &a, &b);
int res = 1000;
for (int i = 0; i <= 50; i ++ )
{
res = min(res, sol(a, b, i));
}
cout << res << endl;
}
return 0;
}
函数求导代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;
const double esp = 1e-5;
LL a, b;
int RealDo(LL a, LL b)
{
int ret = 0;
while (a)
{
ret ++;
a /= b;
}
return ret;
}
int Get(LL a, LL b, LL tarb)
{
if (tarb <= 1 || tarb < b) return 0x3f3f3f3f;
// return (tarb - b) + floor(log(a) / log(tarb)) + 1;
return (tarb - b) + RealDo(a, tarb);
}
double Cal(LL a, LL b)
{
double lga = log(a);
double l = b, r = 2e9, mid = b;
if (lga <= mid * (log(mid) * log(mid)) ) // 不用加
return mid;
while (abs(l - r) >= esp)
{
mid = (l + r) / 2;
if (lga <= mid * (log(mid) * log(mid))) // 高了
r = mid;
else // 低了
l = mid;
}
return mid;
}
int sol(LL a, LL b)
{
double x = Cal(a, b);
return min(Get(a, b, x), min(Get(a, b, x + 1), Get(a, b, x - 1)));
}
int main()
{
int t;
cin >> t;
while (t -- )
{
scanf("%lld%lld", &a, &b);
cout << sol(a, b) << endl;
}
return 0;
}
B. Replace and Keep Sorted
原题信息
http://codeforces.com/contest/1485/problem/B
解题思路
我们单独考虑端点的可能性,已经中间结点不同的次数(可以使用前缀和)
但是一定要仔细考虑什么时候 + 1,什么时候没有+1, 端点是否包括
AC代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, k, q;
int a[N];
int b[N];
int cal(int l, int r)
{
if (l == r) return k - 1;
else
{
int ret = (a[l] - 1) + (a[l + 1] - a[l] - 1) + (k - a[r]) + (a[r] - a[r - 1] - 1);
if (l == r - 1)
return ret;
else
return ret + b[r - 1] - b[l]; // l + 1 ~ r - 1
}
}
int main()
{
cin >> n >> q >> k;
for (int i = 1; i <= n; i ++ )
scanf("%d", &a[i]);
memset(b, 0, sizeof b);
for (int i = 2; i <= n - 1; i ++ )
b[i] = (a[i] - a[i - 1] - 1) + (a[i + 1] - a[i] - 1);
for (int i = 2; i <= n - 1; i ++ )
b[i] += b[i - 1];
while (q -- )
{
static int l, r;
scanf("%d%d", &l, &r);
// cout << "res" << endl << "\t";
cout << cal(l, r) << endl;
}
return 0;
}
C. Floor and Mod
原题信息
http://codeforces.com/contest/1485/problem/C
解题思路
C题吐槽一下,当时复杂度分析错了,
O
(
N
)
O(\sqrt N)
O(N)分析成
O
(
N
)
O(N)
O(N),太绝了
设
⌊
a
b
⌋
=
a
%
b
=
t
,
t
<
b
\lfloor\frac a b\rfloor=a\%b=t, t< b
⌊ba⌋=a%b=t,t<b
那么
a
=
t
⋅
b
+
t
=
(
b
+
1
)
⋅
t
,
A
≥
a
≥
1
,
B
≥
b
≥
1
a=t \cdot b + t=(b+1)\cdot t, A\geq a\geq1,B\geq b\geq1
a=t⋅b+t=(b+1)⋅t,A≥a≥1,B≥b≥1
- 当 t = 1 t=1 t=1时, a = b + 1 , b ∈ [ 2 , B ] a=b+1,b\in [2, B] a=b+1,b∈[2,B]
- 当 t = 2 t=2 t=2时, a = 2 ⋅ ( b + 1 ) , b ∈ [ 3 , B ] a=2\cdot(b+1),b\in [3, B] a=2⋅(b+1),b∈[3,B]
- …
- 当
t
=
i
t=i
t=i时,
a
=
i
⋅
(
b
+
1
)
,
b
∈
[
i
+
1
,
B
]
a=i\cdot(b+1),b\in [i + 1, B]
a=i⋅(b+1),b∈[i+1,B]
此时, i ⋅ ( b + 1 ) ≤ A , b ≤ B , b ≥ i + 1 i \cdot (b + 1)\leq A, b\leq B, b\geq i+1 i⋅(b+1)≤A,b≤B,b≥i+1
⟹ b ∈ [ i + 1 , m i n ( B , ⌊ A i ⌋ − 1 ) ] \Longrightarrow b\in [i + 1, min(B,\lfloor \frac A i \rfloor-1)] ⟹b∈[i+1,min(B,⌊iA⌋−1)]
如果 ⌊ A i ⌋ − 1 \lfloor \frac A i \rfloor-1 ⌊iA⌋−1有前缀和公式的话,是可以进行化简为 O ( 1 ) O(1) O(1)的
最后, b ∈ [ i + 1 , m i n ( B , ⌊ A i ⌋ − 1 ) ] b\in [i + 1, min(B,\lfloor \frac A i \rfloor-1)] b∈[i+1,min(B,⌊iA⌋−1)]可以看出来 i i i的最大值,是无法取到 1 e 9 1e9 1e9的,需要开个根号。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
void Sol(LL A, LL B)
{
LL a, b, tmp, ret = 0;
for (int i = 1; true; i ++ )
{
tmp = min(B, A / i - 1);
// i + 1 ~ tmp
if (i + 1 > tmp) break;
else ret += (tmp - (i + 1) + 1);
}
cout << ret << endl;
}
int main()
{
int t; cin >> t;
while (t -- )
{
static LL a, b;
scanf("%lld%lld", &a, &b);
Sol(a, b);
}
return 0;
}
D. Multiples and Power Differences
原题信息
http://codeforces.com/contest/1485/problem/D
解题思路
可恶啊,这个题目是一个LCM的板子题,没看出来。。。
首先,我们对1~16求LCM = x,我们的 B 数组首先全是x,可以满足
m
u
l
t
i
p
l
e
multiple
multiple的性质,然后我们凑性质三,发现可以直接进行隔项加上
a
i
,
j
4
a_{i, j}^4
ai,j4,而且数据范围小,满足性质1
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 510;
int n, m;
int a[N][N];
int b[N][N];
int gcd(int x, int y)
{
if (y == 0) return x;
else return gcd(y, x % y);
}
int lcm(int x, int y)
{
return LL(x) * y / gcd(x, y);
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &a[i][j]);
int x = 1;
for (int i = 1; i <= 16; i ++ )
{
x = lcm(x, i);
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
b[i][j] = x;
for (int i = 1; i <= n; i ++ )
{
for (int j = (i % 2 == 1 ? 1 : 2); j <= m; j += 2)
{
b[i][j] += (a[i][j] * a[i][j]) * (a[i][j] * a[i][j]);
}
}
// output
for (int i = 1; i <= n; i ++ )
{
printf("%d", b[i][1]);
for (int j = 2; j <= m; j ++ )
printf(" %d", b[i][j]);
puts("");
}
return 0;
}
总结
- 首先考虑暴力,能否直接缩小范围,省时间
- 其次,考虑复杂度的时候,分析准确一些
- 多考虑边界的情况,+1,-1
- 最后题型要把握清楚,数据范围小的时候,有时候会暴力,有时候直接lcm。。。