20241114升达ACM校队训练赛
文章目录
- 20241114升达ACM校队训练赛
- [C - Kousuke's Assignment](https://vjudge.net/problem/CodeForces-2033D)
- 题意描述:
- 解题思路:
- 代码如下:
- [D - Sakurako's Field Trip](https://vjudge.net/problem/CodeForces-2033C)
- 题意描述:
- 解题思路:
- 解题代码:
- [F - New Game](https://vjudge.net/problem/CodeForces-2025C)
- 题目描述:
- 解题思路:
- 代码如下:
- [H - I Love 1543](https://codeforces.com/problemset/problem/2036/D)
- 题目描述:
- 解题思路:
- 代码如下:
- [A - Reverse the Rivers](https://codeforces.com/problemset/problem/2036/E)
- 题目描述:
- 解题思路:
- [B - Alya and Permutation](https://codeforces.com/problemset/problem/2035/C)
- 题意描述:
- 解题思路:
- 代码如下:
- [G - Kar Salesman](https://codeforces.com/problemset/problem/2022/B)
- 题意描述:
- 解题思路:
- 代码如下:
- [E - Black Cells](https://codeforces.com/problemset/problem/2026/B)
- 题意描述:
- 解题思路:
- 代码如下:
C - Kousuke’s Assignment
题意描述:
给出一个数组,计算最大的不重叠美丽段数量:
如果a[l]+a[l+1]+a[l+2]+…+a[r]=0,则该段被认为是美丽的。
解题思路:
如果数组中有一个数字为0,则无需和其他数字结合,本身便是美丽的
-
怎样判断哪一段是完美的?完美的符合怎样的条件
数组: 1 2 3 -3 0 1 0 -1 前缀和: 1 3 6 3 3 4 4 3
我们可以发现如果一段是完美的,那么他的前缀和会与未加上该段时前缀和的值相等(因为完美段和为0)
-
如果数组中有一个数字为0,则无需和其他数字结合,本身便是美丽的
-
因此,我们只需要判断每次的前缀和是否出现过,可以用map来记录sum
-
由于不重复,每次判断出一个完美段后,需要将map清空以及sum归零
代码如下:
#include <bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); map<int,int>mp; void solve() { mp.clear(); int n; cin>>n; int x; int sum=0; int ans=0; for(int i=1;i<=n;i++) { cin>>x; sum+=x; if(x==0||mp[sum]==1||sum==0) { ans++; sum=0; mp.clear(); } else mp[sum]=1; } cout<<ans<<endl; } signed main() { IOS int t; t=1; cin>>t; while(t--) { solve(); } return 0 ; }
D - Sakurako’s Field Trip
题意描述:
每次可以将数组中位置i和位置n-i+1的学生交换,可以进行无限次交换,使得数组中相邻数相同数量最小,输出可达到的最小数量。
解题思路:
-
如果n为2,则判断两个数是否相等,可以直接输出
-
如果大于2,可以从中间向两边以l,r为指向位置判断是否交换
-
l,r怎么取值???
如果n为奇数,则中间的不用变换
如果n为偶数,我们可以发现中间的两个数字交换前后并无差别
因此,l为中间数字(n为奇数,中间数字为1个,n为偶数,中间数字为2个)之前的位置,r为中间的数字后的位置
-
当什么时候进行交换?
当交换后比交换前相邻数的组数在减少时进行交换
怎样判判断是否减少?
e,b分别记录左右两侧交换前与中间靠中间一侧重复数字的个数,同时,c,d分别记录左右两侧交换后与中间靠中间一侧重复数字的个数,通过比较e+b和c+d来决定是否交换
-
最后输出数组中相邻数相同数量即可
解题代码:
#include <bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int a[100010]; void solve() { int n,e=0,b=0,c=0,d=0,t; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } if(n==2) { if(a[1]==a[2]) cout<<"1"<<endl; else cout<<"0"<<endl; } else { int l,r; if(n%2==0) { l=n/2-1; r=n/2+2; } else { l=n/2; r=n/2+2; } while(l>0&&r<=n) { if(a[l]==a[l+1]) e=1; if(a[r]==a[r-1]) b=1; if(a[l]==a[r-1]) c=1; if(a[r]==a[l+1]) d=1; if(e+b>c+d) { t=a[l]; a[l]=a[r]; a[r]=t; } e=0,b=0,c=0,d=0; l--;r++; } int ans=0; for(int i=2;i<=n;i++) { if(a[i]==a[i-1]) ans++; } cout<<ans<<endl; } } signed main() { IOS int t; t=1; cin>>t; while(t--) { solve(); } return 0 ; }
F - New Game
题目描述:
给出两个整数,n和k。n张卡牌,每张卡牌上有一个整数,当前回合他可以拿一张数字为 x 或数字为 x+1 的卡牌,所拿卡牌上写的不同数字的数量不得超过 k。确定在游戏中可以从牌堆中拿到的最大卡牌数量(他在第一回合可以从牌堆中拿任何一张卡牌)
解题思路:
- 将所有卡牌从小到大排序,每个数字出现的次数存在map中
- 用l,r指针记录当前取出卡牌的最大值与最小值,防止超过k
- 类似于滑动窗口,保证所取卡牌不同牌数最大等于k,并且每次结果都与ans比较,取得最大值
代码如下:
#include <bits/stdc++.h> using namespace std; #define int long long #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); const int N=2e5+10; int a[N]; void solve() { int n,k; cin>>n>>k; for(int i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+n+1); map<int,int>m; vector<int>v; for(int i=1;i<=n;i++) { if(m[a[i]]==0) v.push_back(a[i]); m[a[i]]++; } int l=0,r=0; int sum=0; int ans=0; while(1) { if(r>v.size()-1) break; ans=max(ans,m[v[r]]); sum+=m[v[r]]; r++; while(v[r]-v[r-1]==1) { if(r-l>=k) { sum-=m[v[l]]; l++; } sum+=m[v[r]]; ans=max(sum,ans); r++; } sum=0; l=r; } cout<<ans<<endl; } signed main() { IOS int t; t=1; cin>>t; while(t--) { solve(); } return 0 ; }
H - I Love 1543
题目描述:
给出n*m的大墙毯(n,m均为偶数),顺时针遍历所有层,求出地毯所有层中 1543 1543 1543出现的总次数。
解题思路:
- 由于给出的数字并未用空格隔开,我们先进行处理,将其转换为二维数组
- 我们对最外层的四个方向数存入一个容器中,由于它属于一个环,因此应该将最初的3个数再次存入容器的尾部
- 对容器的数进行判断,求出 1543 1543 1543出现的次数
- 由于不止一层,每层判断方式一致,将上述步骤放入循环即可,循环次数即层数为min(m,n)/2
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1010;
int a[N][N];
int ans;
void solve()
{
ans=0;
int m,n;
cin>>m>>n;
string s;
for(int i=1;i<=m;i++)
{
cin>>s;
for(int j=1;j<=s.size();j++)
{
a[i][j]=s[j-1]-'0';
}
}
// for(int i=1;i<=m;i++)
// {
//
// for(int j=1;j<=s.size();j++)
// {
// cout<<a[i][j]<<" ";
// }
// cout<<endl;
// }
for(int k=1;k<=min(m,n)/2;k++)
{
vector<int>v;
for(int j=k;j<=n-k+1;j++)
{
v.push_back(a[k][j]);
}
for(int i=k+1;i<=m-k+1;i++)
{
v.push_back(a[i][n-k+1]);
}
for(int j=n-k;j>=k;j--)
{
v.push_back(a[m-k+1][j]);
}
for(int i=m-k;i>k;i--)
{
v.push_back(a[i][k]);
}
for(int i=0;i<3;i++)
{
v.push_back(v[i]);
}
// for(auto &x:v){
// cout<<x<<" ";
// }
// cout<<endl;
//
for(int i=0;i<v.size();i++)
{
if(i+3>=v.size())
break;
if(v[i]==1&&v[i+1]==5&&v[i+2]==4&&v[i+3]==3)
ans++;
}
}
cout<<ans<<endl;
}
signed main()
{
IOS
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0 ;
}
A - Reverse the Rivers
题目描述:
n
n
n个国家,每个国家有
k
k
k个地区,a[i][j]反映该地的水量。
智者打算为第 i个国家的第 j 个地区和第 (i+1)个国家的第 j个地区之间创建通道经过通道创建,地区的水量将由a[i][j]变为a[i][j]|a[i-1][j].
接下来给出q个要求,找出满足要求的最小国家的编号
解题思路:
- 处理二维数组,将其变为通道创建后的水量
- 异或后数组为递增形式,由于我们需要使用地区的查找,可以将其进行倒置放vector容器,方便接下来查找函数的使用
- 想要满足所有要求,需要注意应该取’>'中的最大值和‘<’中的最小值,与数学答案需要满足多组条件时,取相交类似
- 最终的区间[l,r]即为满足所有要求的值,如果l<=r,则取l的值即为最优,否则就不存在满足所有条件的值。输出“-1”即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//const int N=1e5+10;
//
//int sum=0;
void solve()
{
int m,n,k;
cin>>m>>n>>k;
int a[m+1][n+1];
vector<vector<int>>v(n+1,vector<int>(m+1));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(i>1)
a[i][j]|=a[i-1][j];
v[j][i]=a[i][j];
}
}
while(k--)
{
int p;
cin>>p;
int l=1,r=m;
int x,y;
char s;
while(p--)
{
cin>>x>>s>>y;
if(s=='>')
{
int u=upper_bound(v[x].begin()+1,v[x].end(),y)-v[x].begin();
l=max(l,u);
}
else
{
int w=lower_bound(v[x].begin()+1,v[x].end(),y)-v[x].begin();
r=min(r,w-1);
}
}
if(l<=r)
cout<<l<<endl;
else
cout<<"-1"<<endl;
}
}
signed main()
{
IOS
int t;
t=1;
// cin>>t;
while(t--)
{
solve();
}
return 0 ;
}
B - Alya and Permutation
题意描述:
构造包含n个数的排列p,最大化n次操作后操作后k的值,k的初始值为0.
执行n次操作,在第i次操作(i=1,2,3…n):
· 如果i是奇数,则k=k&p[i]其中 & 表示 按位与操作。
· 如果 i 是偶数,则k=k|p[i] ,其中 | 表示 按位或操作。
解题思路:
- 如果为偶数个,我们将2 1输出接下来按序输出即为所求
- 奇数个时,k通过&与|操作后的最大值,应该为n转化为二进制后的数位,并且各个位全为1转化为10进制的数,(例如10(1010)最大可变为15(1111))
- 奇数个时怎样操作才能使得最后结果为与n二进制相同位数的1呢?
以10为例,我们想得到1111,我们可以1010&1011得到,111又可以由110|11得到,而111又可以由1得到,将这些放到最后,其他的各个位则无所谓 - 但是,6为特例,他会导致前后重复,将其单独输出即可
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//const int N=1e5+10;
//
//int sum=0;
int a[200010];
void solve()
{
int n;
cin>>n;
if(n==6)
{
cout<<"7"<<endl;
cout<<"1 2 4 6 5 3"<<endl;
return;
}
if(n%2!=0)
{
int k=0;
a[1]=2,a[2]=1;
for(int i=3;i<=n;i++)
{
a[i]=i;
}
for(int i=1;i<=n;i++)
{
if(i%2==0)
{
k|=a[i];
}
else
k&=a[i];
}
cout<<k<<endl;
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
else
{
int p;
for(int i=1;i<=n;i++)
{
if(pow(2,i)>n)
{
p=i;
break;
}
}
cout<<pow(2,p)-1<<endl;
int m=pow(2,p-1);
cout<<"2 ";
for(int i=4;i<=n;i++)
{
if(i!=m-1&&i!=m-2&&i!=m)
cout<<i<<" ";
}
cout<<"1 3 "<<m-2<<" "<<m-1<<" "<<m<<endl;
}
}
signed main()
{
IOS
int t;
t=1;
cin>>t;
while(t--)
{
solve();
}
return 0 ;
}
G - Kar Salesman
题意描述:
商店有n种不同型号汽车,第i种有a[i]辆,每位顾客最多买x辆汽车(且这x辆为不同型号),求出可以卖出所有汽车需要的最少顾客量
解题思路:
我们在每一次的减去最大数目型号的过程中,都会去删除其他的值,也就是次大值,然后就得到了一个答案,要不然就是最大值,或者是sum/x向上取整的答案
- 如果按汽车不同型号搭配卖出,则为sum%x==0?sum/x+1:sum/x;
- 否则为最多的汽车数量数目,取两者最大值
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[500010];
void solve()
{
int m,n;
int ma=0;
int sum=0;
cin>>m>>n;
for(int i=1;i<=m;i++)
{
cin>>a[i];
ma=max(ma,a[i]);
sum+=a[i];
}
int p;
if(sum%n==0)
p=sum/n;
else
p=sum/n+1;
cout<<max(ma,p)<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
E - Black Cells
题意描述:
选择两个白色单元格i,j(i!=j&&abs(i-j)<=k),并将它们涂成黑色。给定一个列表a,该列表中的所有单元格必须被涂成黑色。此外,最多只能 涂一个不在此列表中的单元格。你的任务是确定使这一切成为可能的最小值 k。
解题思路:
- 如果n为1,则直接输出1
- 如果n为偶数,则无需借助其他单元格便可以涂完所有单元格,我们取最大差值即可
- n为奇数时,我们应该将奇数位置上的与其他单元格结合,如果偶数位置的改动,则会使偶数前后的数结合,使得差值更大,我们依次尝试每个奇数位置的变换,取最小的一个
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[2010];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
if (n == 1) {
cout << "1" << endl;
return;
} else if (n % 2 == 0) {
int ma = 0;
for (int i = 2; i <= n; i += 2) {
ma = max(ma, a[i] - a[i - 1]);
}
cout << ma << endl;
return;
} else {
int res = 1;
int ans = 1e18;
for (int i = 1; i <= n; i += 2) {
res = 1;
for (int j = 1; j <= n; j += 2) {
if (j == i) {
j--;
continue;
}
res = max(res, a[j + 1] - a[j]);
}
ans = min(ans, res);
}
cout << ans << endl;
}
}
signed main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}