Codeforces Round 987 (Div. 2)题解记录(A~D)
比赛链接:https://codeforces.com/contest/2031
明天再补题面,A题漏看了题目细节卡了到19分才过,一开始如果数组是乱序的就得dp了吧,但是按道理来讲一般A是贪心或者暴力,一直往这两个方向想,结束后才发现,漏看了不递增条件;C题知道9,16,25的情况,但是一直没去想27,卡了50多分钟;D题最后30秒交,有个判断删了就可以过,差点点。今天运势不佳,各种错误并发,太累了。
A. Penchick and Modern Monument
题面:
在繁华的大都会马尼拉的摩天大楼之间,菲律宾最新的诺伊夫购物中心刚刚建成!施工经理潘奇克下令建造一个最先进的纪念碑,拥有 \(n\) 根柱子。
纪念碑的柱子高度可以用一个包含 \(n\) 个正整数的数组 \(h\) 来表示,其中 \(h_i\) 代表第 \(i\) 根柱子的高度,所有 \(i\) 都在 1 和 \(n\) 之间。
潘奇克希望柱子的高度按非递减顺序排列,即对所有 \(i\) 在 1 和 \(n - 1\) 之间,\(h_i \le h_{i + 1}\)。然而,由于混淆,纪念碑建成时柱子的高度实际上是按非递增顺序排列的,即对所有 \(i\) 在 1 和 \(n - 1\) 之间,\(h_i \ge h_{i + 1}\)。
幸运的是,潘奇克可以修改纪念碑,并根据需要多次对柱子执行以下操作:
- 将一根柱子的高度修改为任何正整数。形式上,选择一个索引 \(1 \le i \le n\) 和一个正整数 \(x\)。然后,赋值 \(h_i := x\)。
帮助潘奇克确定使纪念碑的柱子高度变为非递减所需的最小操作次数。
输入:
每个测试包含多个测试用例。第一行包含测试用例的数量 \(t\)(\(1 \le t \le 1000\))。测试用例的描述紧随其后。
每个测试用例的第一行包含一个整数 \(n\)(\(1 \leq n \leq 50\))——柱子的数量。
每个测试用例的第二行包含 \(n\) 个整数 \(h_1, h_2, \ldots, h_n\)(\(1 \le h_i \le n\) 且 \(h_i \ge h_{i+1}\))——柱子的高度。
请注意,给定的数组 \(h\) 是非递增的。
请注意,所有测试用例中 \(n\) 的总和没有限制。
输出:
对于每个测试用例,输出一个整数,表示使柱子的高度变为非递减所需的最小操作次数
样例:
3
5
5 4 3 2 1
3
2 2 1
1
1
——————
4
1
0
思路:就是看哪个数最多,然后n-这个最大值即可。因为翻译问题,我没看到一开始的数组就是不递增的,所以写了个简单dp。
dp版:
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll dp[500][55];
ll a[250000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
ll ans=99999999999;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
for(ll j=1;j<=50;j++)
{
if(a[i]==j)
dp[i][j]=0;
else dp[i][j]=1;
}
for(ll j=1;j<=50;j++)
{
ll u=99999999;
for(ll k=1;k<=j;k++)
{
u=min(dp[i][k],u);
}
dp[i][j]=dp[i-1][j]+u;
if(i==n)
ans=min(ans,dp[i][j]);
}
}
cout<<ans<<endl;
}
}
正常版:
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[66];
int main()
{
ll t;
cin>>t;
while(t--)
{
map<ll,ll>q;
ll n;
cin>>n;
ll ans=0;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
q[a[i]]++;
ans=max(ans,q[a[i]]);
}
cout<<n-ans<<endl;
}
}
B. Penchick and Satay Sticks
题面:
Penchick 和他的朋友 Kohane 正在游览印度尼西亚,他们的下一站是泗水!
在泗水繁忙的小吃摊上,Kohane 买了 \(n\) 根沙爹串并将它们排成一行,其中第 \(i\) 根沙爹串的长度为 \(p_i\)。已知 \(p\) 是长度为 \(n\) 的一个排列。
Penchick 想要按长度递增序排列沙爹串,使得 \(p_i=i\) 对于每个 \(1\le i\le n\)。为了好玩,他们制定了一个规则:他们只能交换长度相差正好 \(1\) 的相邻沙爹串。形式上,他们可以执行以下操作任意次数(包括零次):
- 选择一个索引 \(i\)(\(1\le i\le n-1\)),使得 \(|p_{i+1}-p_i|=1\);
- 交换 \(p_i\) 和 \(p_{i+1}\)。
确定是否可以通过执行上述操作来对排列 \(p\) 进行排序,从而对沙爹串进行排序。
\(^{\text{∗}}\)长度为 \(n\) 的排列是一个由 \(1\) 到 \(n\) 的 \(n\) 个不同整数组成的数组,以任意顺序排列。例如,\([2,3,1,5,4]\) 是一个排列,但 \([1,2,2]\) 不是排列(\(2\) 在数组中出现了两次),\([1,3,4]\) 也不是排列(\(n=3\) 但数组中有 \(4\))。
输入:
每个测试包含多个测试用例。第一行包含测试用例的数量 \(t\)(\(1 \le t \le 2 \cdot 10^5\))。测试用例的描述紧随其后。
每个测试用例的第一行包含一个整数 \(n\)(\(1 \le n \le 2 \cdot 10^5\))——沙爹串的数量。
每个测试用例的第二行包含 \(n\) 个整数 \(p_1, p_2, \ldots, p_n\)(\(1 \le p_i \le n\))——排列 \(p\) 表示沙爹串的长度。
保证所有测试用例中 \(n\) 的总和不超过 \(2 \cdot 10^5\)。
输出:
对于每个测试用例,如果可以通过执行操作对排列 \(p\) 进行排序,则输出 "YES"。否则,输出 "NO"。
你可以以任何大小写字母输出答案(大写或小写)。例如,字符串 "yEs"、"yes"、"Yes" 和 "YES" 将被视为肯定的响应。
样例:
2
4
2 1 3 4
4
4 2 3 1
——————
YES
NO
思路:通过题目要求可以推得,如果一个数离他原本位置距离大于1,则一定无解,比如3 1 2,可以变为3 2 1 ,2 3 1,但是3永远不会到顺序位置3
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[250000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
map<ll,ll>q;
ll ans=0;
for(ll i=1;i<=n;i++)
{
cin>>a[i];
if(abs(a[i]-i)<=1)continue;
else ans=1;
}
if(ans==0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
C. Penchick and BBQ Buns
题面:
Penchick 喜欢两样东西:平方数和港式烧烤包!为了他的生日,Kohane 想要将它们结合起来作为礼物:\(n\) 个烧烤包从左到右排列。有 \(10^6\) 种可用的烧烤包馅料,编号从 \(1\) 到 \(10^6\)。为了确保 Penchick 会喜欢这份礼物,Kohane 有几个目标:
- 没有一种馅料被使用恰好一次;也就是说,每种馅料要么根本不出现,要么至少出现两次。
- 对于任何两个具有相同馅料的包子 \(i\) 和 \(j\),它们之间的距离,即 \(|i-j|\),必须是一个完全平方数\(^{\text{∗}}\)。
帮助 Kohane 找到一个有效的选择包子馅料的方法,或者确定是否不可能满足她的目标!如果有多种解决方案,请打印其中任何一个。
\(^{\text{∗}}\) 一个正整数 \(x\) 是一个完全平方数,如果存在一个正整数 \(y\) 使得 \(x = y^2\)。例如,\(49\) 和 \(1\) 是完全平方数,因为 \(49 = 7^2\) 和 \(1 = 1^2\) 分别。另一方面,\(5\) 不是一个完全平方数,因为没有整数的平方等于 \(5\)。
输入:
每个测试包含多个测试用例。第一行包含测试用例的数量 \(t\)(\(1 \le t \le 2 \cdot 10^5\))。测试用例的描述紧随其后。
每个测试用例的唯一一行包含一个整数 \(n\)(\(1 \le n \le 2 \cdot 10^5\))——烧烤包的数量。
保证所有测试用例中 \(n\) 的总和不超过 \(2 \cdot 10^5\)。
输出:
对于每个测试用例,如果没有有效的填充选择,输出 \(-1\)。否则,输出 \(n\) 个整数,其中第 \(i\) 个整数代表第 \(i\) 个烧烤包的填充物。如果有多个解决方案,请打印其中任何一个。
样例:
2
3
12
——————
-1
1 2 3 6 10 2 7 6 10 1 7 3
思路:对于偶数来讲,很好可以发现,以连续两个数放进去就可以,且不会超过1e6,如1 1,2 2 ,3 3。。。
对于奇数:特殊情况是9,16,25,所以27及以上均有解,对于27,23设为同一个数,第9,第16,第25设为同一个数。其他位置就按照偶数形式的规则放置就好了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[250000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
ll k=n;
for(ll i=1;i<=n;i++)a[i]=0;
if(n&1)
{
if(n>=27)
{
ll cnt=1;
while(n>27)
{
a[n]=cnt;
a[n-1]=cnt;
n-=2;
cnt++;
}
a[27]=a[23]=cnt;
cnt++;
a[26]=a[10]=a[1]=cnt;
cnt++;
for(ll i=1;i<=27;i++)
{
if(a[i]==0)
{
a[i]=cnt;
a[i+1]=cnt;
cnt++;
}
}
for(ll i=1;i<=k;i++)
cout<<a[i]<<" ";
cout<<endl;
}
else
cout<<-1<<endl;
}
else
{
ll cnt=1;
for(ll i=1;i<=n;i+=2)
{
cout<<cnt<<" "<<cnt<<" ";
cnt++;
}
cout<<endl;
}
}
}
D. Penchick and Desert Rabbit
题面:
致力于挑战自己的极限,Penchick 挑战自己在阿拉伯沙漠的正午阳光下生存!
在沿着一条线性绿洲徒步旅行时,Penchick 发现了一只沙漠兔子准备沿着一排棕榈树跳跃。有 \(n\) 棵树,每棵树的高度由 \(a_i\) 表示。
兔子可以从第 \(i\) 棵树跳到第 \(j\) 棵树,如果以下条件中的一个为真:
- \(j < i\) 且 \(a_j > a_i\):兔子可以跳到更高的树上。
- \(j > i\) 且 \(a_j < a_i\):兔子可以跳到更矮的树上。
对于每个 \(i\) 从 \(1\) 到 \(n\),确定兔子从第 \(i\) 棵树开始时可以达到的所有树中的最大高度。
输入:
第一行包含测试用例的数量 \(t\)(\(1 \le t \le 5 \cdot 10^5\))。测试用例的描述紧随其后。
每个测试用例的第一行包含一个整数 \(n\)(\(1 \le n \le 5 \cdot 10^5\))——树的数量。
每个测试用例的第二行包含 \(n\) 个整数 \(a_1, a_2, \ldots, a_n\)(\(1 \le a_i \le n\))——树的高度。
保证所有测试用例中 \(n\) 的总和不超过 \(5 \cdot 10^5\)。
输出:
对于每个测试用例,输出 \(n\) 个整数。第 \(i\) 个整数应该包含兔子从第 \(i\) 棵树开始时可以到达的所有树中的最大高度。
样例:
5
4
2 3 1 4
5
5 4 3 2 1
4
2 1 1 3
4
1 1 3 1
8
2 4 1 6 3 8 5 7
——————
3 3 3 4
5 5 5 5 5
2 2 2 3
1 1 3 3
8 8 8 8 8 8 8 8
思路:首先从后往前走,可以最贪心的拿到此时变的数的最大值。用前缀数组处理维护左边的最大数,然后对于一个位置,如果右边pre[i-1]=ans or a[i]=ans,则他是能变成目前最大值的,此时我用一个变量u维护当前条件的数的最小值,如果pre[i-1]!=ans and a[i]!=ans,此时比较pre[i-1],a[i]与u的大小,只要左边有比u大的情况存在,则一定是可以跳到u再跳到上一次的ans,就继续走,如果当上述条件都不符合时,重新设定u等于当前数,ans=max(pre[i-1],a[i]),就行了。因为右边与它没关系了
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"// 交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
ll ksm(ll x, ll y)
{
ll ans = 1;
while (y)
{
if (y & 1)
{
ans = ans % mod * (x % mod) % mod;
}
x = x % mod * (x % mod) % mod;
y >>= 1;
}
return ans % mod % mod;
}
ll gcd(ll x, ll y)
{
if (y == 0)
return x;
else
return gcd(y, x % y);
}
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
ll a[654000];
ll b[625000];
ll pre[625000];
ll sub[625000];
int main()
{
fio();
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
//ll ans=0;
sub[n+1]=999999999999;
for(ll i=1;i<=n;i++)cin>>a[i],pre[i]=max(a[i],pre[i-1]),b[i]=a[i];
ll u=99999999999;
ll ans;
for(ll i=n;i>=1;i--)
{
if(i==n)
{
u=min(u,a[i]);
ans=max(a[i],pre[i-1]);
b[i]=ans;
}
if(pre[i-1]==ans||a[i]==ans)
{
b[i]=ans;
u=min(u,a[i]);
}
else if(pre[i-1]>u||a[i]>u)
{
b[i]=ans;
u=min(u,a[i]);
}
else
{
ans=max(pre[i-1],a[i]);
u=a[i];
b[i]=ans;
}
}
for(ll i=1;i<=n;i++)cout<<b[i]<<" ";
cout<<endl;
}
}