A - Diagonals
解题思路
这道题就是让你把一堆东西按照对角线长度的大小来放,优先较长的,那么对于正方形来说,它的反对角线的长度划分为n、n-1、n-1、n-2、n-2…那么除了第一个其他的长度都是两个,那么可以预处理一下第一种情况。之后就是按照规律来安排剩余的对角线,我是通过判断奇偶数来判断是否要更行对角线的长度。
AC代码
#include <bits/stdc++.h>
using namespace std;
void solve () {
int n,k;
cin>>n>>k;
int res = 0;
bool flag = 1;
if(k == 0) {
cout<<0<<endl;
return ;
}
if(k <= n) {
cout<<1<<endl;
return;
}
k -= n;
res ++;
while(k > 0) {
if(res % 2) {
n--;
}
k -= n;
res++;
}
cout<<res<<endl;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
总结
对于这种情况需要交替进行处理操作的时候,可以通过判断奇偶来进行处理。
B1 - Bouquet (Easy Version)
解题思路
这道题思路是非常清晰的,首先排序,然后通过动态区间来查找满足条件的那个区间,可以遍历所有的值来当做左边界,然后通过二分寻找右边界,这样时间复杂度就到了 O ( n 2 ) O(n^2) O(n2),注意要满足两个条件,最后还要处理一下结果是0的情况。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
ll a[N],sum[N];
ll n,m;
bool check(int x,int k) {
ll ans = sum[x] - sum[k-1];
if(a[x] - a[k] <= 1ll && ans <= m) {
return true;
}else {
return false;
}
}
ll dich(ll a[],ll sum[],int l,int r,int k) {
while(l < r) {
int mid = (l + r + 1) >> 1;
if(check(mid,k)) l = mid;
else r = mid - 1;
}
return sum[l] - sum[k-1];
}
void solve () {
memset(a,0,sizeof a);
memset(sum,0,sizeof sum);
ll res = 0;
cin>>n>>m;
for(int i = 1;i <= n;i++) {
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i = 1;i <= n;i++) {
sum[i] = a[i] + sum[i-1];
}
// for(int i = 1;i <= n;i++) {
// cout<<sum[i]<<" "<<a[i]<<endl;
// }
// cout<<endl;
//
for(int i = 1;i <= n;i++) {
// cout<<"结果:"<<dich(a,sum,i,n,i)<<endl;
if(a[i] > m) break;
res = max(res,dich(a,sum,i,n,i));
}
// cout<<"================> ";
cout<<res<<endl;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
总结
比赛的时候就剩下最后一个情况判断了,完全没有想到结果还可能是0的时候,完美错失了一次涨分的机会。
B2 - Bouquet (Hard Version)
解题思路
跟B1题目唯一不同的就是,这个花朵有数量限定,这时候就需要动态更新来找出合适的数量进行组合,并且在找数量时,还要注意的是花瓣数之差是不能超过1的,所以在更新的时候首先找到 i
最多可以用几个?然后找 i+1 最多可以用几个,最后根据当前找到的这个区间动态移动,找到最大值,就似乎 少选几个 i 多选几个 i+1的,那么最后结果就是
m
x
=
m
a
x
(
m
,
i
∗
v
1
+
(
i
+
1
)
∗
v
2
+
m
i
n
(
v
1
,
b
[
i
]
−
v
2
)
)
mx = max(m,i*v_1+(i+1)*v_2+min(v_1,b[i]-v_2))
mx=max(m,i∗v1+(i+1)∗v2+min(v1,b[i]−v2))。
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N],b[N];
void solve () {
map<int,int> mp;
ll n,m;
scanf("%lld %lld",&n,&m);
for(int i = 1;i <= n;i++) {
scanf("%lld",&a[i]);
}
for(int i = 1;i <= n;i++) {
scanf("%lld",&b[i]);
}
for(int i = 1;i <= n;i++) {
mp[a[i]] += b[i];
}
sort(a+1,a+n+1);
ll mx = 0;
for(int i = 1;i <= n;i++) {
ll key = 0;
ll val = a[i];
ll remine = mp[a[i]];
remine = min(remine,m / val);
ll v1 = remine;
key = val * remine;
val = a[i] + 1;
remine = mp[val];
remine = min(remine,(m - key) / val);
key += remine * val;
ll v2 = mp[val] - remine;
key += min(v1,v2);
if(key > m) key = m;
mx = max(mx,key);
}
printf("%lld\n",mx);
}
int main () {
int t;
scanf("%d",&t);
while(t--) {
solve();
}
return 0;
}
总结
这题就跟组合问题一样,那个选点,另一个选点,然后看看有没有扩展的可能,最后判断可扩展的范围内能取到的最大值?有点想组合问题。
C - Squaring
解题思路
模拟之后发现这道题可以从左到右依次枚举,然后与前面的值判断是否需要进行平方,如果需要一直平方到大于等于前面的值。但是由于数据过大,不能进行平方计算,该怎么把这个平方的操作进行缩小呢?
首先如果当前值需要平方的话,假设需要
c
i
c_i
ci次平方,那么当前数就可以表示为
a
i
2
c
i
a_i^{2^{c_i}}
ai2ci,那么前面的数可以表示为
a
i
−
1
2
c
i
−
1
a_{i-1}^{2^{c_{i-1}}}
ai−12ci−1,现在我们想要找到
a
i
2
c
i
≥
a
i
−
1
2
c
i
−
1
a_i^{2^{c_i}} \geq a_{i-1}^{2^{c_{i-1}}}
ai2ci≥ai−12ci−1的
c
i
c_i
ci,我们可以通过化简来求,对两边同时取对数:
2
c
i
l
o
g
a
i
≥
2
c
i
−
1
l
o
g
a
i
−
1
2^{c_i}log{a_i} \geq 2^{c_{i-1}}log{a_{i-1}}
2cilogai≥2ci−1logai−1
c
i
l
o
g
2
+
l
o
g
l
o
g
a
i
≥
c
i
1
l
o
g
2
+
l
o
g
l
o
g
a
i
−
1
c_ilog2+logloga_i \geq c_{i_1}log2+logloga_{i-1}
cilog2+loglogai≥ci1log2+loglogai−1
这时候就可以根据这个公式来找到相应的 c i c_i ci的值了,因为每一个值都可能会扩大不妨设一个数组来存储每一个值对应的平方次数,最后记录一下总的即可。
在找 c i c_i ci的时候应该要用到二分来查找最终的值。
AC代码
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
typedef long double ld;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N],f[N];
bool check(ll a,ll f1,ll b,ll f2) {
ld loga = log10(1.0 * a),logb = log10(1.0 * b);
ld logloga = log10(loga),loglogb = log10(logb);
ld log2 = log10(2.0);
return (f1 * log2 + logloga + eps < f2 * log2 + loglogb);
}
void solve () {
memset(a,0,sizeof a);
int n;
cin>>n;
for(int i = 1;i <= n;i++) {
cin>>a[i];
}
for(int i = 1;i <= n;i++) f[i] = 0;
ll res = 0;
for(int i = 2;i <= n;i++) {
if(a[i-1] > a[i] && a[i] == 1) {
cout<<-1<<endl;
return;
}
if(check(a[i],f[i],a[i-1],f[i-1])) {
ll l = 1,r = 2e6;
while(l < r) {
ll mid = l + r >> 1ll;
if(check(a[i],mid,a[i-1],f[i-1])) {
l = mid + 1;
}else {
r = mid ;
}
}
f[i] = l;
}
res += f[i];
}
// cout<<"=============> ";
cout<<res<<endl;
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
总结
纯纯指数化简,将指数运算的范围缩小,通过取对数的方式来缩小,接下来就用到了指数运算的数学知识了。以后遇见这些计算不了的数要想想怎么缩小范围来计算。
CF最近比赛怎么赛前没报名还要等10分钟在进行报名呀,又等了十分钟,又是掉分的比赛。