为了应付开学考试摸了两星期,这场小白月赛感觉比较简单,正好适合复建。
A.先交换
题意
给一个数组a,每次操作可以交换满足 i < j 且 a[i] > a[j] 的两个数,问经过多少次操作使得a[1] 为奇数。
思路
水题,如果开头不是奇数,往后遍历找一个奇数小于a[1]的,找不到就是-1,找到就是1。开头是奇数直接是0。
Code
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
cin >> n;
vector<int>a(n + 1);
for(int i = 1;i <= n;i ++)
cin >> a[i];
if(a[1] & 1) cout << 0 << endl;
else
{
for(int i = 2;i <= n;i ++)
if(a[i] % 2 == 1 && a[i] < a[1])
{
cout << 1 << endl;
return ;
}
cout << "-1" << endl;
return ;
}
}
int main()
{
int tt;
cin >> tt;
while(tt --)
solve();
}
B.再交换
题意
给两个长度为n的十进制数a,b,可以选择i、j,交换ai和bj,ai是从左到右第i个数位,bj同理。问交换一次能否使a<b?
思路
对于两个数,高位第一个不一样的数位就决定了两个数的大小(129****一定大于120****),遍历寻找第一个不一样的数位i,如果有ai > bi,就把该位交换,如果ai < bi,说明已经满足条件a < b,就交换前面任意一位相同的,注意i == 0的特判
Code
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n;
string A,B;
cin >> n >> A >> B;
for(int i = 0;i < A.size();i ++)
{
if(A[i] == B[i]) continue;
if(A[i] > B[i])
{
cout << i + 1 << " " << i + 1 << endl;
return ;
}
else if(A[i] < B[i])
{
if(i >= 1) cout << i << " " << i << endl;
else cout << i + 2 << " " << i + 2 << endl;
return ;
}
}
}
int main()
{
int tt;
cin >> tt;
while(tt --)
solve();
}
C.空洞骑士
题意
给一个[0,1e9]的数轴,数轴上有m个吉欧,小骑士会从起点s出发,收集所有吉欧后到达终点t,要求给出一个s和t,使得小骑士采取最优策略的情况下行进距离最长
思路
考虑了两种情况,首先是起点和终点相邻在数轴最边界,这样距离大概是2 * [距起点和终点最远的吉欧的距离]。
第二种是起点在吉欧中间,想着小骑士可以多往返几次,但实际上由于小骑士会采取最优策略,他肯定会先走到离终点最远的吉欧的位置再顺路直接到终点,这样的距离大概是[距起点最远的吉欧距离 + 距终点最远吉欧的距离],显然不如第一种
继续考虑第一种情况,显然发现起点和终点可以取[0,1e9],[0,1],[1e9,1e9 - 1]三种情况,分类讨论一下即可(注意可能多个吉欧位于同一个点)
Code
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int m,flag = 0;
cin >> m;
vector<int>a(m + 1);
for(int i = 1;i <= m;i ++)
{
cin >> a[i];
if(i > 1 && a[i] != a[i - 1])
flag = 1;
}
if(!flag)
{
//cout << "?" << endl;
cout << 0 << " " << (int)1e9 << endl;
return ;
}
int MIN = 1e9 + 7,MAX = 0;
for(int i = 1;i <= m;i ++)
{
MIN = min(MIN,a[i]);
MAX = max(MAX,a[i]);
}
int info1 = (int)2e9 - 1 - 2 * MIN;
int info2 = 2 * MAX - 1;
if(info1 >= info2) cout << (int)1e9 << " " << (int)1e9 - 1 << endl;
else cout << 0 << " " << 1 << endl;
}
int main()
{
// int tt;
// cin >> tt;
// while(tt --)
solve();
}
D.障碍
题意
在[0,n]的数轴内有m个障碍,现在可以从中移除多个障碍,称移除的障碍个数为x,称区间[0,n]内最长段长度为L,要求最大化L -
思路
首先考虑L -
,如果
大于L,答案一定不会更优,所以x的最大值实际上只有
,接下来考虑暴力枚举。
没想到怎么枚举,看题解学到一个O(n^2)的枚举,第一层枚举第i个障碍,第二层枚举移除了前j个障碍,答案即为a[i] - a[i - j + 1] - j * j,维护一个最大值即可。
注意处理边界问题,可以插入一个a[0] = 0和a[m + 1] = m,直接进行枚举就可以防止边界问题出错。
Code
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n,m;
cin >> n >> m;
vector<int>pos(m + 2);
for(int i = 1;i <= m;i ++) cin >> pos[i];
pos[0] = 0,pos[m + 1] = n;
int res = 0;
for(int i = 1;i <= m + 1;i ++)
{
for(int j = 0;j <= min(i - 1,(int)sqrt(n));j ++)
{
res = max(res,pos[i] - pos[i - j - 1] - j * j);
}
}
cout << res << endl;
}
int main()
{
// int tt;
// cin >> tt;
// while(tt --)
solve();
}
E.生成树与路径
题目链接
题意
给定n,m,构造一个无向带权简单联通图,满足该图的最小生成树边权和等于1~n的最短路长度,同时要求边权为[1,m]的序列。
思路
首先考虑 最小生成树,不难想到最小生成树的边权和为n * (n - 1) / 2,也就是连一条线,边权分别为(1,2,....,n - 1),要想最短路也是这个值,就需要对于1 - 3,2 - 4,3 -5等等边的边权,都小于等于链1-3,2-4等等的边权和。
然后写一写就会发现,就可以发现我们按照两点相差从小到大的顺序分配边权,就可以实现上述效果。
e.g. n = 4,m = 6
w{1,2} = 1,w{2,3} = 2,w{3,4} = 3;
w{1,3} = 4,w{2,4} = 5;
w{1,4} = 6
Code
#include<bits/stdc++.h>
using namespace std;
void solve()
{
int n,m;
cin >> n >> m;
int cnt = 1;
for(int dif = 1;dif <= n - 1;dif ++)
{
for(int j = 1;j + dif <= n;j ++)
{
cout << j << " " << j + dif << " " << cnt ++ << endl;
if(cnt > m) break;
}
if(cnt > m) break;
}
}
int main()
{
int tt;
cin >> tt;
while(tt --)
solve();
}
F.球球大作战
题意太长懒得写了
思路
1.首先可以想到,对于ai > aj,如果aj能找到一个合法顺序,那么ai肯定也能找到一个合法顺序。先排序,然后可以二分去找最小的能找到合法顺序的球球。
2.可以注意到每次碰撞后小球一定不会变大,那么当check(ai)时,我们让除了ai的所有球球全都互相碰撞,最后得到的一定是一个最小的球球,如果ai比最小的球球还小,那么就无法找到合法顺序
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int n;
pair<int,int> a[maxn];
bool check(int id)
{
int temp = (id == n) ? a[n - 1].first : a[n].first;
for(int i = n;i >= 1;i --)
{
if(i == id) continue;
temp = (temp + a[i].first) / 2;
}
if(a[id].first >= temp) return true;
else return false;
}
int main()
{
cin >> n;
for(int i = 1;i <= n;i ++) cin >> a[i].first,a[i].second = i;
vector<int>vis(n + 1);
sort(a + 1,a + 1 + n);
int l = 1,r = n;
while(l < r)
{
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
for(int i = l;i <= n;i ++)
vis[a[i].second] = 1;
for(int i = 1;i <= n;i ++)
if(vis[i]) cout << "1";
else cout << "0";
cout << endl;
}