第二周打卡,这次选了新疆的ccpc题目,大家早上起的太早了,都没咋睡醒,感觉这次题目都是题面十分简单,然后做起来相当复杂。
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
现有一个长度为 nnn 的数组 aaa,从左到右依次编号为 111 到 nnn,其中第 i(1≤i≤n)i (1 \leq i \leq n)i(1≤i≤n) 个元素的值为 aia_iai。
Sang 最初位于第 111 个元素的位置,他想要到达第 nnn 个元素的位置,即从最左端到最右端。
为此,他需要在数组上进行移动。在一次操作中,他可以向任意其他位置移动。如果 Sang 想从第 iii 个元素的位置移动到第 jjj 个元素的位置,就需要大小为 ∣ai−aj∣+∣i−j∣(1≤i,j≤n,i≠j)\left| a_i - a_j \right| + \left| i - j \right| (1 \leq i, j \leq n, i \neq j)∣ai−aj∣+∣i−j∣(1≤i,j≤n,i=j) 的代价。
Sang 想要知道,他从第 111 个元素的位置移动到第 nnn 个元素的位置所需要的最小代价。
显然,代价最小的移动方案可能会有多种。为了这次冒险足够奇妙,他还想要知道满足代价最小的前提下,他最多可以进行多少次移动。
输入描述:
输入的第一行为一个正整数 n(1≤n≤106)n (1 \leq n \leq 10^6)n(1≤n≤106),表示数组的长度。
接下来一行 nnn 个空格分隔的正整数 ai(1≤i≤n,1≤ai≤106)a_i (1 \leq i \leq n, 1 \leq a_i \leq 10^6)ai(1≤i≤n,1≤ai≤106),表示数组中第 iii 个元素的值。
输出描述:
输出一行两个空格分隔的整数表示答案。
第一个数字表示最小代价,第二个数字为最小代价下的最大移动次数。
示例1
输入
复制4 2 4 9 7
4 2 4 9 7输出
复制8 2
8 2
思路:其实这题就是个数学题,数学好的同学一目了然,代价包括距离之差和值之差两部分,距离之差其实就等于从起点到终点的距离,而值之差其实画个坐标图就出来了,只要是函数是单调的,那么y的值的和就是一定的,这一点可以用微分证明,但其实画图更加直观,再有就是只要不是单调的,y的值的和一定会多出来一部分,所以我们要找的最小代价其实就是找一个能够到达终点的最长的单调子序列,其实最长也就是尽量让x能分成更多份,具体在代码上也就是尽可能地让x的坐标小一点。
#include "iostream"
#include "algorithm"
#include "vector"
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int a[N];
vector<int> v;
int main(){
int n;
cin>>n;
ll ans = 0, res = n - 1, xx;
for(int i = 0; i < n; i++){
cin>>a[i];
}
if(a[0] > a[n - 1]) reverse(a, a + n);
xx = a[0];res += a[n - 1] - a[0];
for(int i = 1; i < n; i ++){
if(a[i]<a[0]||a[i]>a[n-1])continue;
auto idx = upper_bound(v.begin(), v.end(),a[i]);
if(idx == v.end()) v.push_back(a[i]);
else *idx = a[i];
}
cout<<res<<" "<<v.size()<<endl;
return 0;
}
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
一个正整数可以分解为若干个正整数的平方和。记 f(x)f(x)f(x) 为 xxx 最少可以被分解为 f(x)f(x)f(x)xxx 个正整数的平方和。特别地,f(x2)=1f(x^2)=1f(x2)=1。
例如:7=22+12+12+127=2^2+1^2+1^2+1^27=22+12+12+12,且不存在小于等于 333 个正整数的平方和为 777,所以 f(7)=4f(7) = 4f(7)=4。
求 ∏1≤i≤nf(i) mod 998244353\prod_{1 \leq i \leq n} f(i) \bmod 998244353∏1≤i≤nf(i)mod998244353。输入描述:
输入一行一个正整数 n(1≤n≤105)n (1 \leq n \leq 10^5)n(1≤n≤105)。输出描述:
输出一行一个整数表示答案。示例1
输入
复制4
4输出
复制6
6示例2
输入
复制27
27输出
复制936673279
936673279
思路:其实这题没啥说的,就是一个dp的题目,然后就是记得赋初始值,每个数最大就是全一的组成,剩下的dp寻找最小的,每一个x1都是上一个x0加某一个平方得来的,相应的 f(x)的值会加一,这样的话就比较是当前最小还是加过来最小就可以了。
#include "iostream"
using namespace std;
const int N = 1e5 + 3;
int f[N], b[N];
int main(){
int n;
cin>>n;
for(int i = 1; i <= 316; i ++){
b[i] = i * i;
}
for(int i = 1; i < N; i ++){
f[i] = N;
}
f[1] = 1;
f[0] = 0 ;
for(int i = 2; i <= n; i++){
for(int j = 1; j <= 316; j ++){
if(i >= b[j]) f[i] = min(f[i - b[j] ]+ 1, f[i]);
else break;
}
}
long long ans = 1;
for(int i = 1; i <= n; i++){
// cout<<f[i]<<endl;
ans = ans * f[i] % 998244353;
}
cout<<ans<<endl;
return 0;
}
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
让我们复习一下排列的定义:一个长度为 nnn 的排列是一个包含 nnn 个元素的数组,对于 1∼n1 \sim n1∼n 的所有整数均在数组中恰好出现一次。
现有一个长度为 nnn 的排列 ppp,从左到右依次编号为 111 到 nnn,其中第 i(1≤i≤n)i (1 \leq i \leq n)i(1≤i≤n) 个元素的值为 pip_ipi。
如果数组中的两个不同位置,前面的数字比后面的数字严格大,则称其为一个逆序对。
我们定义一个排列的同步值为它的逆序对数,即整个排列中有多少个逆序对。
请你找出另一个排列,与输入排列 ppp 的长度和同步值均一致。
输入描述:
输入的第一行为一个正整数 n(1≤n≤500)n (1 \leq n \leq 500)n(1≤n≤500),表示排列的长度。
接下来一行 nnn 个空格分隔的正整数 pip_ipi,表示排列中第 iii 个元素的值。保证输入的排列合法。
输出描述:
输出一行 nnn 个空格分隔的正整数表示答案。
如果有多个满足条件的排列,你可以输出任意一个。
如果找不到另一个长度和同步值均一致的排列,则输出一行一个整数 −1-1−1。
示例1
输入
复制5 4 1 5 2 3
5 4 1 5 2 3输出
复制5 1 2 4 3
5 1 2 4 3说明
排列的长度和同步值均为 555。示例2
输入
复制3 1 2 3
3 1 2 3输出
复制-1
-1
思路:这题其实是一个思维题,很直观可以看出来的是升序和降序的序列是不可重排的,但是,一个凸起的顺序和一个凹下去的,(例如:1 3 2 和3 1 2)这种的,只需要稍加变换就能实现逆序对不变而顺序改变。
#include "iostream"
#include "algorithm"
using namespace std;
const int N = 1e3 + 1;
int a[N];
int main(){
int n, flag =0;
cin>>n;
for(int i = 0; i < n; i++){
cin>>a[i];
}
for(int i = 1; i < n - 1; i++){
if(a[i] > a[i - 1] == a[i] > a[i + 1]) {
int t = 2 * (a[i] > a[i - 1] == a[i - 1] > a[i + 1]) - 1;
swap(a[i - t], a[i]);
swap(a[i + t], a[i]);
flag = 1;
break;
}
}
if(!flag) cout<<"-1"<<endl;
else {
for(int i = 0 ; i < n; i ++)
cout<<a[i]<<" ";
}
return 0;
}