CF 907
(A—D题解)
A:
思路:要修改区间 [ 2 i , 2 i + 1 − 1 ] [2^i,2^{i+1}-1] [2i,2i+1−1] 中的某个数,必须修改整个区间的数,也就是说区间 [ 2 i , 2 i + 1 − 1 ] [2^i,2^{i+1}-1] [2i,2i+1−1] 上的数,他们之间相对大小不会变,所以,我们要让整个数组都变成不下降的,当且仅当所有的这样的区间都是不下降的才可以达到目的
AC_code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 22;
int n;
int a[N];
void solve()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
int l = 1 , r = 1;
bool flag = true;
while(l<=n)
{
for(int i=l;i<r;i++)
{
if(a[i]>a[i+1]) flag = false;
}
l = r + 1;
r *= 2;
r = min(r,n);
}
if(flag) cout << "YES\n";
else cout << "NO\n";
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
B:
思路:
设
a
<
b
a<b
a<b ,则如果整数 x
满足
x
∣
2
a
x|2^a
x∣2a,则必然有
x
∣
2
b
x|2^b
x∣2b ,且 KaTeX parse error: Undefined control sequence: \centernot at position 13: (x+2^{a-1}\ \̲c̲e̲n̲t̲e̲r̲n̲o̲t̲ ̲|\ 2^{b}),那么如果 x
数组中出现了 6
那么 6
后面的大于 6
的数字都不会再对原始数组产生影响,所以,我们只要从 x
数组开头开始,只保留递减的数字即可,由于 x
数组中每个数在
[
1
,
30
]
[1,30]
[1,30] 之间,所以我们最多只剩下 30
个数,接下里暴力模拟即可。
Ac_code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 100;
int n,q;
int a[N],x[N],p[33],cnt;
void solve()
{
cnt = 0;
cin >> n >> q;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=q;i++) cin >> x[i];
for(int i=1;i<=q;i++)
{
if(!cnt)
{
p[++cnt] = x[i];
}
if(p[cnt]>x[i])
{
p[++cnt] = x[i];
}
}
for(int i=1;i<=cnt;i++)
{
int mod = pow((int)2,p[i]);
for(int j=1;j<=n;j++)
{
if(a[j]%mod==0) a[j] += (mod/2);
}
}
for(int i=1;i<=n;i++) cout << a[i] << ' ';
cout << '\n';
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
C:
思路:
在操作次数相同的情况下,技能放的越少(但总归要放掉),我们产生的总伤害就越多,我们的总伤害必须要大于等于怪物的总血量才能获胜,如果总伤害大于怪物的总血量(此时肯定是普攻积累的能量有剩余),那么我们必然会有更优的方案(先把多余能量用来放技能),所以最优的方案,总伤害是恰好等于怪物总血量的,那么在总伤害一样的情况下,要使得操作次数最少,就要让每次放技能消耗的能量尽可能多,然而,我们放技能的能量局限于怪的总血量,那么我们优先考虑用技能来对付高血量的怪物,利用小怪来攒能量。当攒的能量刚好能够消灭当前血量最多的怪物时,则使用技能消灭他,如果当前只剩一种怪物,则先积攒能量,当积攒的能量等于剩余血量或者 剩余血量 -1 时,先使用技能,再普攻打死。
AC_code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 100;
int n;
int a[N];
int ans = 0;
void solve()
{
ans = 0;
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
sort(a+1,a+n+1);
int l = 1 , r = n;
int x = 0;
while(l<r)
{
int temp = min(a[r]-x,a[l]);
ans += temp;
a[l] -= temp;
x += temp;
if(a[l]==0) l++;
if(x==a[r]) a[r] = 0 ,ans ++ , r -- , x = 0;
}
if(l==r&&a[l])
{
if(x<a[l]&&a[l]>1)ans += (a[l]-x+1)/2 + 1;
else ans ++;
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t--)
{
solve();
}
return 0;
}
D:
思路:
根据题意归纳出 f[x]
,g[x]
,的公式为:
f
(
x
)
=
l
o
g
2
x
g
(
x
)
=
l
o
g
(
l
o
g
2
x
)
x
f(x) = log_2\ x \\ g(x) = log_{(log_2x)}\ x
f(x)=log2 xg(x)=log(log2x) x
这两个函数有很多 x
对应的值是相同的,比如当
x
∈
[
2
i
,
2
i
+
1
−
1
]
x\in[2^i,2^{i+1}-1]
x∈[2i,2i+1−1] 时,
f
(
x
)
=
i
f(x)=i
f(x)=i,
g
(
x
)
=
l
o
g
i
x
g(x)=log_ix
g(x)=logix。所以我们考虑分块,将相同的值放在一块考虑。
- 将区间 [ l , r ] [l,r] [l,r] 分成若干个区间 [ l , 2 i − 1 ] [l,2^i-1] [l,2i−1]、 [ 2 i , 2 i + 1 − 1 ] [2^i,2^{i+1}-1] [2i,2i+1−1]、 [ 2 i + 1 , 2 i + 2 − 1 ] [2^{i+1},2^{i+2}-1] [2i+1,2i+2−1] … [ 2 k , r ] [2^k,r] [2k,r]。
- 对于上述的每个区间内 l o g 2 x log_2x log2x 都是定值,假设区间 [ s l , s r ] [sl,sr] [sl,sr] 是上述区间中的一个,且 l o g 2 s l = l o g 2 s r = i log_2\ sl =log_2\ sr = i log2 sl=log2 sr=i。
- 则对于任意 x ∈ [ s l , s r ] x\in[sl,sr] x∈[sl,sr],有 g ( x ) = l o g i x g(x)=log_i\ x g(x)=logi x。
- 我们再考虑将区间 [ s l , s r ] [sl,sr] [sl,sr] 分成若干个区间 [ s l , i j − 1 ] , [ i j , i j + 1 − 1 ] , . . . . . . , [ i k , s r ] [sl,i^j-1],[i^j,i^{j+1}-1],......,[i^k,sr] [sl,ij−1],[ij,ij+1−1],......,[ik,sr](在本题中,由于 l o g 2 x ≥ 2 log_2\ x \ge 2 log2 x≥2 ,所以 [ s l , s r ] [sl,sr] [sl,sr] 最多分成两块。
- 在
4
中划分的区间, g ( x ) g(x) g(x) 的值均相同,只要算出左端点对应的 g ( x ) g(x) g(x) ,在乘上区间长度,就可以得到这一段区间 g ( x ) g(x) g(x) 的和了。 - 时间复杂度 O ( 2 q l o g 2 l e n ) O(2qlog_2len) O(2qlog2len)。
AC_code:
#include<bits/stdc++.h>
using namespace std;
std::ostream& operator<<(std::ostream& os, __int128 p) {
vector<char> a;
if(p<0)os<<'-',p=-p;
if(p==0)a.push_back(0);
while (p)a.push_back(p % 10), p /= 10;
for (int i = a.size() - 1; i >= 0; i--)os << (signed)(a[i]);
return os;
}
std::istream& operator>>(std::istream& os, __int128& p) {
p = 0;
string s;
os >> s;
for(int i=0;i<s.size();i++)if(s[i]!='-')p=p*10+s[i]-'0';
if(s[0]=='-')p=-p;
return os;
}
#define int __int128
const int mod = 1e9 + 7;
int get_e(int a,int b) // 返回最大的满足 a^x <= b 的 x;
{
int res = 0;
int temp = 1;
while (temp < b) {
temp *= a;
res++;
}
if (temp > b) return res - 1;
return res;
}
int get_num(int l,int r) // g(x) 在 [l,r] 上的和
{
int ans = 0, sr, sum = 1;
int i = get_e(2, l);
int s = get_e(i, l);
while (l <= r) {
while (sum <= l) sum *= i;
sr = min(sum - 1, r);
ans = (ans + (sr - l + 1) * s) % mod;
s++;
l = sr + 1;
}
return ans;
}
void solve() {
int ans = 0, l, r, sum = 1, sr;
cin >> l >> r;
while (l <= r) // 先按f(x)的值分块
{
while (sum <= l) sum *= 2;
sr = min(sum - 1, r);
ans = (ans + get_num(l, sr)) % mod; // 再次分块计算 g(x) 的和
l = sr + 1;
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
}