Codeforces Round #701 (Div. 2) ABC
作者:互联网
Codeforces Round #701 (Div. 2) ABC
A - Add and Divide
题意
给定两个整数 a , b a, b a,b ,你可以进行两种操作:
- a = ⌊ a b ⌋ a = \lfloor \frac{a}{b} \rfloor a=⌊ba⌋ (用 ⌊ a b ⌋ \lfloor \frac{a}{b} \rfloor ⌊ba⌋ 替换 a a a);
- b = b + 1 b = b + 1 b=b+1(使 b b b 加1)
找出能使 a = 0 a = 0 a=0 的最小操作次数。
思路
首先,操作 2 2 2 一定是先于操作 1 1 1 进行的,因为操作 1 1 1 是指数级别减少,而 2 2 2 是线性减少, 2 2 2 放在 1 1 1 之前就能使 2 2 2 的影响从线性变为指数级别;
容易看出,若设总操作次数为 y y y,其中操作 2 2 2 的次数为 x x x ,则 y y y 关于 x x x 的函数在取值范围内先减后增(我不太能证明出来,但确实是这个规律)。所以我们直接从 x = 0 x = 0 x=0 开始遍历尝试,求出每次的操作数,找其最小值,找到函数拐点后推出循环即可。
有一个很坑的地方是,不能用C++的log
函数,当然也可能是我用错方式了。第一次写的时候我的代码是 ll ans = log(a) / log(b) + i + 1;
,这一步在有一个数据点上一直出错,表现为 log(a) / log(b) + i + 1
的值没有问题,但是赋值给 ans
就变了,非常奇怪,后来直接换成遍历求log就过了。
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e4 + 19;
const ll mod = 1e9 + 7;
int calc(int a, int b)
{
int ans = 0;
while(a >= b)
{
a /= b;
ans++;
}
return ans;
}
int main()
{
int T;
cin >> T;
while(T--)
{
int a, b;
cin >> a >> b;
if(a < b)
{
cout << 1 << endl;
continue;
}
int ans = INF;
for(int i = 0; b + i <= a; i++)
{
if(b + i == 1)
continue;
int tmp = i + 1 + calc(a, b + i);
if(tmp <= ans)
{
ans = tmp;
}
else
{
break;
}
}
ans = min(ans, a - b + 2);
cout << ans << endl;
}
return 0;
}
B - Replace and Keep Sorted
题意
两个数组被称为 k k k 相似,当且仅当:
- 都严格递增;
- 长度相等;
- 所有元素都是 1 1 1~ k k k 之间的正整数(包含边界);
- 他们只在一个位置值不同。
给定 k k k 和一个严格递增且每个元素值都不超过 k k k 、不小于 1 1 1 的数组 a a a ,询问 q q q 次,每次给定两个数 l , r l, r l,r ,要求你回答对于 [ l , r ] [l,r] [l,r] 范围内的 a a a 的子数组(即数组 [ a l , a l + 1 , … , a r ] [a_l, a_{l + 1}, …, a_r] [al,al+1,…,ar]),有多少个数组和这个数组 k k k 相似。
思路
乍一看很复杂,细想很简单的题。
因为只能有一个位置值不同,所以我们先枚举可能出现不同的位置,那么显然是从 l l l 到 r r r 的每个位置都可以是不同的位置。
那这样的话,问题就变成了求每个点可以变化的值的个数,我们只需要预处理出每个点可以变化的范围即可,这个范围显然是 [ m a x ( a i − 1 + 1 , 1 ) , m i n ( a i + 1 − 1 , k ) ] − 1 [max(a_{i - 1} + 1, 1),min(a_{i + 1} - 1, k)] - 1 [max(ai−1+1,1),min(ai+1−1,k)]−1 ,即以上一个数为底,下一个数为顶,其中自己这个数不能取(不然就不变了)。
最后,这个数据范围并不允许我们每次从 l l l 到 r r r 遍历求和,所以也预先求出前缀和数组,查询时直接相减即可。
这题的边界我是单独拎出来处理的,即1个数、2个数、3个数的情况单独处理,没有想到很好的办法就if/else了。
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;
int a[N];
int dif[N];
int num[N];
int main()
{
int n, q, k;
cin >> n >> q >> k;
cin >> a[0];
a[n] = k;
dif[0] = a[0] - 1;
for(int i = 1; i < n; i++)
{
cin >> a[i];
dif[i] = a[i] - a[i - 1] - 1;
dif[i - 1] += a[i] - a[i - 1] - 1;
if(i == n - 1)
dif[i] += max(k - a[i] - 1, 0);
}
num[0] = dif[0];
for(int i = 1; i < n; i++)
{
num[i] = num[i - 1] + dif[i];
}
for(int i = 0; i < q; i++)
{
int l, r;
cin >> l >> r;
l--;
r--;
if(l == r)
{
cout << k - 1 << endl;
}
else if(r - l == 1)
{
cout << a[r] - 2 + (k - a[l] - 1) << endl;
}
else if(r - l == 2)
{
cout << a[l + 1] - 2 + (k - a[r - 1] - 1) + dif[l + 1] << endl;
}
else
{
cout << a[l + 1] - 2 + (k - a[r - 1] - 1) + num[r - 1] - num[l] << endl;
}
}
return 0;
}
C - Floor and Mod
题意
对给定的 x , y x, y x,y ,找到所有的 a ∈ [ 1 , x ] , b ∈ [ 1 , y ] a \in [1, x], \ b \in [1, y] a∈[1,x], b∈[1,y] ,满足 ⌊ a b ⌋ = a m o d b \lfloor \frac{a}{b} \rfloor = a \mod b ⌊ba⌋=amodb ,输出这样的数对一共有几对。
思路
问题其实就是:
-
先求出对于 b i ∈ [ 2 , y ] b_i \in [2, y] bi∈[2,y] ( 1 1 1 肯定不行),有 c i c_i ci 个数,可使 a = ( b + 1 ) ∗ k ( k ∈ [ 1 , b − 1 ] ) a = (b + 1) * k \ \ (k \in [1, b - 1]) a=(b+1)∗k (k∈[1,b−1]) 成立。所有能使原式成立的 k k k 都满足:
1 ≤ k ≤ b − 1 , 1 b + 1 ≤ k ≤ x b + 1 1 \leq k \leq b - 1 ,\ \ \frac{1}{b + 1} \leq k \leq \frac{x}{b + 1} 1≤k≤b−1, b+11≤k≤b+1x
因此,不难得出:
c i = m i n ( b − 1 , x b + 1 ) c_i = min(b - 1, \frac{x}{b + 1}) ci=min(b−1,b+1x) -
a n s = ∑ i = 2 y c i ans = \sum_{i = 2}^{y} c_i ans=∑i=2yci。
但是第一步直接这样硬算是会超时的,所以我们对
c
i
c_i
ci 究竟会是
b
−
1
b- 1
b−1 还是
x
b
+
1
\frac{x}{b + 1}
b+1x 进行分类讨论,这步的计算过程略,结果如下:
c
i
=
{
b
−
1
,
1
≤
c
i
≤
x
+
1
这部分直接等差数列求和
x
b
+
1
,
x
+
1
≤
c
i
≤
x
这部分整除分块求和
c_i=\begin{cases} b - 1, \ \ \ \ \ 1 \leq c_i \leq \sqrt{x + 1}&\text{这部分直接等差数列求和}\\\frac{x}{b + 1}, \ \ \ \ \ \sqrt{x + 1} \leq c_i \leq x &\text{这部分整除分块求和}\end{cases}
ci={b−1, 1≤ci≤x+1
b+1x, x+1
≤ci≤x这部分直接等差数列求和这部分整除分块求和
第二部分的整除分块,即对于一定区间内的
b
b
b ,
x
b
+
1
\frac{x}{b + 1}
b+1x 取整后的值是相同的。所以这部分放在一起计算,节省时间,少了这步会超时。
我觉得有必要把整除分块的复习提上日程。
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define please return
#define AC 0
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 1e6 + 19;
const ll mod = 1e9 + 7;
ll calc(int x, int y)
{
int sq = min((int)sqrt(x + 1), y);
ll ans = sq * (sq - 1) / 2;
for(int b = sq + 1; b <= y && x / (b + 1) != 0;)
{
int tmp = x / (b + 1);
int nxtb = min(x / tmp, y + 1);
ans += (nxtb - b) * tmp;
b = nxtb;
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int x, y;
scanf("%d%d", &x, &y);
printf("%lld\n", calc(x, y));
}
please AC;
}
总结
以后double转int的精度问题真的需要好好思考再下手,这个A题确实不明不白卡了很久才发现是log函数的问题(也要做好复杂度估算)。
多多包涵,共同进步
标签:ABC,frac,int,ll,701,leq,ans,Div,define 来源: https://blog.csdn.net/qq_49451105/article/details/113811002