CF1016赛后总结
文章目录
- 前言
- T1:Ideal Generator
- T2:Expensive Number
- T3:Simple Repetition
- T4:Skibidi Table
- T5:Min Max MEX
- T6:Hackers and Neural Networks
- T7:Shorten the Array
前言
由于最近在半期考试,更新稍微晚了一点,还望大家见谅 (保佑我考好一点)。
T1:Ideal Generator
题目翻译:
如果 [ a 1 , a 2 , … , a k ] = [ a k , a k − 1 … , a 1 ] [a_1,a_2,\dots,a_k]=[a_k,a_{k-1}\dots,a_1] [a1,a2,…,ak]=[ak,ak−1…,a1],我们称一个由 k k k 个正整数组成的数组 a a a 为回文数组。例如,数组 [ 1 , 2 , 1 ] [1,2,1] [1,2,1] 和 [ 5 , 1 , 1 , 5 ] [5,1,1,5] [5,1,1,5] 是回文的,而数组 [ 1 , 2 , 3 ] [1,2,3] [1,2,3] 和 [ 21 , 12 ] [21,12] [21,12] 则不是。
我们称一个数 k k k 为理想生成器,如果任意整数 n ( n ≥ k ) n(n\ge k) n(n≥k) 都可以表示为恰好长度为 k k k 的回文数组的元素之和。数组中的每个元素都必须大于 0 0 0。
例如,数字 1 1 1 是一个理想生成器,因为任何自然数 n n n 都可以使用数组 [ n ] [n] [n] 生成。然而,数字 2 2 2 不是理想生成器——不存在长度为 2 2 2 且总和为 3 3 3 的回文数组。
判断给定的数字 k k k 是否为理想生成器。
附上样例:
输入:
5
1
2
3
73
1000
输出:
YES
NO
YES
YES
NO
从样例看出好像是奇数就行。
这道题其实很好想,我们这么思考:当 k k k 是奇数时,中间必然会有一个单独的数与自己相对应,所以中间这个数很自由,可以是任何正整数,但如果 k k k 是偶数,那么就要左右能各分一半,否则就不成立。(看不懂的自己思考。)
代码当课后习题了……
T2:Expensive Number
题目翻译:
一个正整数 n n n 的成本被定义为该数字nn除以其各位数字之和的结果。
例如,数字 104 104 104 的成本是 104 1 + 0 + 4 = 20.8 \cfrac{104}{1+0+4}=20.8 1+0+4104=20.8,数字 111 111 111 的成本是 111 1 + 1 + 1 = 37 \cfrac{111}{1+1+1}=37 1+1+1111=37。
给定一个不含前导零的正整数 n n n。你可以从数字 n n n 中移除任意数量的数字(包括不移除),使得剩余数字至少包含一位且严格大于零。剩余数字不能重新排列。因此,你可能会得到含有前导零的数字。
例如,给定数字 103554 103554 103554。如果你决定移除数字 1 1 1、 4 4 4 和一个数字 5 5 5,你将得到数字 035 035 035,其成本为 035 0 + 3 + 5 = 4.375 \cfrac{035}{0+3+5}=4.375 0+3+5035=4.375。
为了使数字的成本尽可能小,你需要移除的最少数字数量是多少?
附上样例:
输入:
4
666
13700
102030
7
输出:
2
4
3
0
又是一道水题……
题目中说了:要让成本尽可能的小。那我们看看成本最小是多少嘛。
毫无疑问,当然是 1 1 1 了,当这个数只有一位时正好满足(不信可以试试看)。
所以,题目其实就是在变相的询问把一个数变成一位数所删掉的最小数字数量。
因为可以有前导零,所以对于某一位数 i i i,从 i − 1 i-1 i−1 到 1 1 1 需要把所有数字删掉,而从 n n n 到 i i i 只需要把不是零的数删掉就行了。
现在最主要的问题就在于这个 i i i 是第几位。
既然我们要尽可能的少删数,那么我们就要尽可能的多优化,怎么优化呢?
对于 i i i 后面来讲,所有的数都会被删,没有例外,所以无法优化。而对于 i i i 前面来讲,只要不是 0 0 0 才会被删,也就是说是 0 0 0 就不会被删咯。这就是我们优化的地方:让尽可能多的 0 0 0 到前面去。所以 i i i 就是从低到高第一个不是 0 0 0 的那一位,然后写代码就对了。
代码也很简单,又多了一道课后习题……
T3:Simple Repetition
题目翻译:
帕莎热爱质数!在尝试寻找生成质数的新方法时,他对网上发现的一个算法产生了兴趣:
- 要获得新数字 y y y,只需将数字 x x x 的十进制表示(不含前导零)重复 k k k 次。
例如,当 x = 52 x=52 x=52 且 k = 3 k=3 k=3 时,得到 y = 525252 y=525252 y=525252;当 x = 6 x=6 x=6 且 k = 7 k=7 k=7 时,得到 y = 6666666 y=6666666 y=6666666。
帕莎非常希望生成的数字 y y y 是质数,但他还不知道如何验证这个算法生成的数字是否为质数。请帮助帕莎判断 y y y 是否为质数!
附上样例:
输入:
4
52 3
6 7
7 1
1 7
输出:
NO
NO
YES
NO
这题挺简单,我直接把我的题解搬下来:
首先最好想的情况: k = 1 k=1 k=1 时,这时候直接判断 n n n 是不是质数就对了,让我们来看其他情况。
如果 n = 1 n=1 n=1 且 k ≠ 1 k\not=1 k=1,那么我们可以得到的是除了 k = 2 k=2 k=2 的情况,其他情况下都是合数,具体证明大家可以自己去搜,过程过长,不在这里赘述了。
如果 n ≠ 1 n\not=1 n=1 且 k ≠ 1 k\not=1 k=1,那么这个数就必定是质数,原因:任何一个 a 1 a 2 a 3 … a 1 a 2 a 3 … ⏟ k 个循环 ‾ \overline{\underbrace{a_1a_2a_3\dots a_1a_2a_3\dots}_{k\text{个循环}}} k个循环 a1a2a3…a1a2a3… 的数必定能写成这样的形式: a 1 a 2 a 3 … ‾ × 100 … ⏟ n 个数 100 … ⏟ n 个数 … ⏟ k − 1 个循环 1 \overline{a_1a_2a_3\dots}\times\underbrace{\underbrace{100\dots}_{n\text{个数}}\underbrace{100\dots}_{n\text{个数}}\dots}_{k-1\text{个循环}}1 a1a2a3…×k−1个循环 n个数 100…n个数 100……1,所以它必然是一个合数。
代码实现:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,k;
bool pd(int x)
{for(int i=2;i*i<=x;i++){if(x%i==0){return false;}}return true;
}
signed main()
{cin>>t;while(t--){cin>>n>>k;if(n>=1&&k>1){if(n==1&&k==2){cout<<"YES"<<endl;}else{cout<<"NO"<<endl;}}else if(n>1&&k==1){cout<<(pd(n)?"YES":"NO")<<endl;}else if(n==1&&k==1){cout<<"NO"<<endl;}}return 0;
}
T4:Skibidi Table
题目翻译:
瓦迪姆喜欢用整数填充方形表格。但今天他想出了一种有趣的填充方式!例如,我们有一个大小为 2 × 2 2\times2 2×2 的表格,行号从上到下递增,列号从左到右递增。我们在左上角单元格放置 1 1 1,右下角放 2 2 2,左下角放 3 3 3,右上角放 4 4 4。这就是他全部的乐趣所在!
幸运的是,瓦迪姆现在有一个大小为 2 n × 2 n 2^n\times2^n 2n×2n 的表格。他计划用 1 1 1 到 2 2 n 2^{2n} 22n 的升序整数来填充它。为了填充如此大的表格,瓦迪姆会将其划分为 4 4 4 个相同的小方形表格,首先填充左上角的子表,然后是右下角的子表,接着是左下角的子表,最后是右上角的子表。每个子表会继续被划分成更小的子表,直到表格尺寸缩小到 2 × 2 2\times2 2×2 为止,此时他会按照上述顺序进行填充。
现在瓦迪姆迫不及待要开始填充表格,但他有 q q q 个两类问题:
- 位于第 x x x 行第 y y y 列的单元格中的数字是多少;
- 数字 d d d 会出现在哪个单元格坐标中。
请帮助回答瓦迪姆的问题。
看到的第一眼:分治。
第二眼……额,没有第二眼了(已经 A C \color{green}{AC} AC 了)。
这道题非常非常非常简单,对于第一种提问,我们只需要设一个 dfs
就行,然后带上五个参数,分别表示当前在哪一行、当前在哪一列、当前格子的尺寸、当前的取值下限、当前的取值上限,然后我们就可以写出如下转移:
{ dfs1 ( x , y , l 2 , s l , s l + ( s r − s l + 1 ) 4 − 1 ) if 1 ≤ x ≤ l 2 , 1 ≤ y ≤ l 2 dfs1 ( x − l 2 , y − l 2 , l 2 , s l + ( s r − s l + 1 ) 4 , s l + ( s r − s l + 1 ) 2 − 1 ) if l 2 < x ≤ l , l 2 < y ≤ l dfs1 ( x − l 2 , y , l 2 , s l + ( s r − s l + 1 ) 2 , s l + ( s r − s l + 1 ) 4 × 3 − 1 ) if l 2 < x ≤ l , 1 ≤ y ≤ l 2 dfs1 ( x , y − l 2 , l 2 , s l + ( s r − s l + 1 ) 4 × 3 , s r ) if 1 ≤ x ≤ l 2 , l 2 < y ≤ l \begin{cases}\operatorname{dfs1}(x,y,\frac{l}{2},sl,sl+\frac{(sr-sl+1)}{4}-1)&\operatorname{if}1\le x\le \frac{l}{2},1\le y \le \frac{l}{2}\\\operatorname{dfs1}(x-\frac{l}{2},y-\frac{l}{2},\frac{l}{2},sl+\frac{(sr-sl+1)}{4},sl+\frac{(sr-sl+1)}{2}-1)&\operatorname{if}\frac{l}{2}\lt x\le l,\frac{l}{2}\lt y \le l\\\operatorname{dfs1}(x-\frac{l}{2},y,\frac{l}{2},sl+\frac{(sr-sl+1)}{2},sl+\frac{(sr-sl+1)}{4}\times3-1)&\operatorname{if}\frac{l}{2}\lt x\le l,1\le y \le \frac{l}{2}\\\operatorname{dfs1}(x,y-\frac{l}{2},\frac{l}{2},sl+\frac{(sr-sl+1)}{4}\times3,sr)&\operatorname{if}1\le x\le \frac{l}{2},\frac{l}{2}\lt y \le l\end{cases} ⎩ ⎨ ⎧dfs1(x,y,2l,sl,sl+4(sr−sl+1)−1)dfs1(x−2l,y−2l,2l,sl+4(sr−sl+1),sl+2(sr−sl+1)−1)dfs1(x−2l,y,2l,sl+2(sr−sl+1),sl+4(sr−sl+1)×3−1)dfs1(x,y−2l,2l,sl+4(sr−sl+1)×3,sr)if1≤x≤2l,1≤y≤2lif2l<x≤l,2l<y≤lif2l<x≤l,1≤y≤2lif1≤x≤2l,2l<y≤l
(打这一段真不容易……)
第二种带六个参数,分别是当前在哪一行、当前在哪一列、当前格子的尺寸、当前的取值下限、当前的取值上限、当前的值(这个就是输入的那个数,一直不变),然后再手推一下就行了。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,q,pw_2[66];
void dfs1(int x,int y,int l,int sl,int sr)
{if(l==1){cout<<sl<<endl;return;}if(x>=1&&x<=l/2&&y>=1&&y<=l/2){dfs1(x,y,l/2,sl,sl+(sr-sl+1)/4-1);}else if(x>l/2&&x<=l&&y>l/2&&y<=l){dfs1(x-l/2,y-l/2,l/2,sl+(sr-sl+1)/4,sl+(sr-sl+1)/2-1);}else if(x>l/2&&x<=l&&y>=1&&y<=l/2){dfs1(x-l/2,y,l/2,sl+(sr-sl+1)/2,sl+(sr-sl+1)/4+(sr-sl+1)/2-1);}else{dfs1(x,y-l/2,l/2,sl+(sr-sl+1)/2+(sr-sl+1)/4,sr);}
}
void dfs2(int x,int y,int s,int l,int sl,int sr)
{if(l==1){cout<<x<<" "<<y<<endl;return;}if(s>=sl&&s<=sl+(sr-sl+1)/4-1){dfs2(x,y,s,l/2,sl,sl+(sr-sl+1)/4-1);}else if(s>=sl+(sr-sl+1)/4&&s<=sl+(sr-sl+1)/2-1){dfs2(x+l/2,y+l/2,s,l/2,sl+(sr-sl+1)/4,sl+(sr-sl+1)/2-1);}else if(s>=sl+(sr-sl+1)/2&&s<=sl+(sr-sl+1)/2+(sr-sl+1)/4-1){dfs2(x+l/2,y,s,l/2,sl+(sr-sl+1)/2,sl+(sr-sl+1)/2+(sr-sl+1)/4-1);}else{dfs2(x,y+l/2,s,l/2,sl+(sr-sl+1)/2+(sr-sl+1)/4,sr);}
}
string s;
signed main()
{pw_2[0]=1;for(int i=1;i<=65;i++){pw_2[i]=pw_2[i-1]<<1;}cin>>t;while(t--){cin>>n>>q;int x=0,y=0;while(q--){cin>>s;if(s=="->"){cin>>x>>y;dfs1(x,y,pw_2[n],1,pw_2[2*n]);}else{cin>>x;dfs2(1,1,x,pw_2[n],1,pw_2[n*2]);}}}return 0;
}
T5:Min Max MEX
题目翻译:
给定一个长度为 n n n 的数组 a a a 和一个数字 k k k。
子数组定义为数组中一个或多个连续元素组成的序列。你需要将数组 a a a 分割成 k k k 个互不重叠的子数组 b 1 , b 2 , … , b k b_1,b_2,\dots,b_k b1,b2,…,bk,这些子数组的并集等于整个数组。此外,你需要最大化 x x x 的值,该值等于所有子数组的最小 MEX ( b i ) \operatorname{MEX}(b_i) MEX(bi)(针对 i ∈ [ 1.. k ] i\in[1..k] i∈[1..k])。
MEX ( v ) \operatorname{MEX}(v) MEX(v) 表示数组 v v v 中未出现的最小非负整数。
触发关键词:最小的最大。
第一眼算法:二分答案。
这个二分答案我就不多说了,我们主要看这个判断函数怎么写。
题目中还有一条信息我们没用:切成 k k k 份。
正向思路:直接枚举,看看从那切成 k k k 份满足题意。
这样的话还不如不写二分答案……
逆向思路:按照当前二分出来的 MEX \operatorname{MEX} MEX 来分割,看看是否比 k k k 份多。
非常好的方案,只需要循环一下然后看看能分割成几份就行了!
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,k,a[200006];
bool check(int x)
{int t=0,num=0;vector<int>v(x+6,0);//标记数组for(int i=1;i<=n;i++){if(!v[a[i]]&&a[i]<=x){v[a[i]]=1;if(a[i]<x){num++;}}if(num>=x){t++;for(int j=0;j<v.size();j++){v[j]=0;}num=0;}}return t>=k;
}
signed main()
{cin>>t;while(t--){cin>>n>>k;for(int i=1;i<=n;i++){cin>>a[i];}int l=0,r=n,mid=0,ans=0;while(l<=r){mid=l+r>>1;if(check(mid)){ans=mid;l=mid+1;}else{r=mid-1;}}cout<<ans<<endl;}return 0;
}
T6:Hackers and Neural Networks
(题目太长了,直接截了个图过来……(懒癌发作!))
据说很简单,但我没做……
T7:Shorten the Array
对我来讲超纲了,感兴趣的同学可以尝试一下。