Codeforces Round #642 (Div. 3)【ABCDEF】(题解)
涵盖知识点:思维、dp
比赛链接:传送门
A - Most Unstable Array
题意: 要求构造长度为\(n\)的非负数组使得和为\(m\)且\(\sum\limits_{i=1}^{n-1} |a_i - a_{i+1}|\)最大化
题解: \(0,m,0,0,0,0\ldots\)。特判\(1,2\)。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while(t--) {
int n, m;
cin >> n >> m;
if (n >= 3)cout << 2 * m << "\n";
else cout << m * (n - 1) << "\n";
}
return 0;
}
B - Two Arrays And Swaps
题意: 至多\(k\)次机会可以互换数组\(a,b\)的任何一个数。求\(a\)的最大和。
题解: \(a\)最小的换\(b\)最大的,直到\(a\)全部比\(b\)大。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=110;
int a[maxn],b[maxn];
int main(){
int t;cin>>t;
while(t--) {
int n,k;
cin>>n>>k;
int sum=0;
for(int i=0;i<n;i++)cin>>a[i],sum+=a[i];
for(int i=0;i<n;i++)cin>>b[i];
sort(a,a+n);
sort(b,b+n);
reverse(b,b+n);
for(int i=0;i<k;i++){
if(a[i]>=b[i])break;
sum+=b[i]-a[i];
}
cout<<sum<<"\n";
}
return 0;
}
C - Board Moves
题意: \(n\times n\)的方格依次标号(\(n\)是奇数)。每次操作可以将一个方格中的一个数移动到与其相邻的方格(包括斜对角)。求最少几次操作使得某个方格含有全部数字。
题解: 全移到中间。每一圈距离中心的距离相等。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=110;
typedef long long ll;
int main(){
int t;cin>>t;
while(t--) {
int n;
cin>>n;
ll ans=0;
for(int i=1;i<=n/2;i++)ans+=1ll*i*i*8;
cout<<ans<<"\n";
}
return 0;
}
D - Constructing the Array
题意: 给定初始全0数组。现在对于每次操作,要求找到最长的全0子串(相等长度选左边那个)。将其中间(偶数偏左)的数赋值为当前操作的次数。
题解: 优先队列维护。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
int ans[maxn];
struct Node{
int l,r;
bool operator <(const Node &b)const{
return b.r-b.l!=r-l?l-r>b.l-b.r:l>b.l;
}
Node()=default;
Node(int _l,int _r){l=_l,r=_r;}
};
priority_queue<Node> q;
int main(){
int t;cin>>t;
while(t--) {
memset(ans,0,sizeof ans);
int n;
cin>>n;
q.push(Node{1,n});
int cnt=0;
while(!q.empty()){
Node tmp=q.top();
q.pop();
int mid=(tmp.l+tmp.r)/2;
ans[mid]=++cnt;
if(tmp.l<mid)q.push(Node(tmp.l,mid-1));
if(tmp.r>mid)q.push(Node(mid+1,tmp.r));
}
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
cout<<"\n";
}
return 0;
}
E - K-periodic Garland
题意: 给定\(01\)串,每次操作可以使\(0\)变为\(1\)或者\(1\)变成\(0\)。求最少几次操作使得所有的相邻\(1\)的间隔为\(k\)。
题解: 首先我们计算出原串中的\(1\)的个数为\(cnt\)。然后枚举\(mod\ k\)意义下的每一位为\(1\)的情况所需个数。注意如果一开始或某个位置需要从\(0\)变为\(1\)的次数大于之前原本是\(1\)的个数,我们可以认为前面的序列全\(0\)。这样次数更小一些。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int t;
cin >> t;
while (t--) {
int n, k;
cin >> n >> k;
string s;
cin >> s;
int cnt=count(s.begin(),s.end(),'1');
ll ans = 1e18;
for (int mod = 0; mod < k; mod++) {
ll here = cnt;
ll delta = 0;
for (int i = mod; i < n; i += k) {
if (s[i] == '0') delta++;
else delta--;
delta = min(delta, 0ll);
ans = min(ans, here + delta);
}
}
cout << ans << '\n';
}
}
F - Decreasing Heights
题意: \(n\times m\)的地图每个位置都有一个值,每次只能向下或者向右走,且对应的方格的值必须为当前方格的值加\(1\).现在每次操作可以使得一个方格的值减\(1\)。问最少几次操作可以使得从\((1,1)\)走到\((n,n)\)。
题解: 通过枚举每一个位置的现有值,我们可以确定当前地图所有合法路径应该存在的值。再dp一下就行了。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
ll a[110][110],dp[110][110];
ll calc(ll st){
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++)
dp[i][j]=1e18;
}
if (a[1][1]>=st) dp[1][1]=a[1][1]-st;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if ((i==1) && (j==1)) continue;
if (a[i][j]>=(st+i+j-2)){
ll now=a[i][j]-(st+i+j-2);
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+now;
}
}
}
return dp[n][m];
}
int main(){
int t;cin>>t;
while (t--){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
cin>>a[i][j];
}
ll ans=1e18;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
ans=min(calc(a[i][j]-(i+j-2)),ans);
}
cout<<ans<<"\n";
}
return 0;
}