CDZSC_2022寒假个人训练赛21级(5)题解
- 简单
- F 模拟
- G 模拟
- I 数学
- 中等
- A 模拟
- B 枚举
- C 数学
- 困难
- D 模拟
- E 贪心、构造、数据结构
- H DP、矩阵快速幂
A Nearest Minimums CodeForces - 911A
题意
给你一个数组a,问你其中两个最小值的的最小距离是多少。
题解
两个循环,先找最小值,然后再找最小距离。
AC代码
int s[100005];
int main() {
int n;
scanf("%d", &n);
int min = 1e9;
for (int i = 0; i < n; i++) {
scanf("%d", s + i);
min = min < s[i] ? min : s[i];
}
int ans = 1e9,last=-1;
for (int i = 0; i < n; i++) {
if (s[i] == min) {
if (last != -1) {
ans = ans < i - last ? ans : i - last;
}
last = i;
}
}
printf("%d\n", ans);
return 0;
}
B Two Cakes CodeForces - 911B
题意
分蛋糕,有两种蛋糕分 n 份,满足如下条件:
- 所有蛋糕都被分出去了
- 一份中至少有一个蛋糕
- 一份中不能有两种不同的蛋糕
使每份,最小块数最大化,问最小块是多少。
题解
数据很小,枚举第一个蛋糕分几份,取最小值的最大值。
AC代码
int main() {
int n,a,b;
scanf("%d%d%d", &n,&a,&b);
int ans = 0;
for (int i = 1; i <n; i++)
ans=max(ans,min(a/i,b/(n-i)));
printf("%d\n", ans);
return 0;
}
C Three Garlands CodeForces - 911C
题意
给你 3 个数 \(k_1,k_2,k_3\) 问你,\(\{i\in \{0,1,2,3...\} |x+i\cdot k_1,y+ i\cdot k_2,z+ i\cdot k_3\;\;\;\;x,y,z\in \{N,0\} \}\)能否覆盖正整数集合。
题解
很容易想到就只有以下4种解
- 1 x x
- 2 2 x
- 3 3 3
- 4 4 2
枚举判断下就好了
也很好理解,有1那表示那个为1的灯一直亮,肯定行,有两个2,这两个灯可以交替亮,也肯定行,3个3也是同理,4,4,2 的情况,两个4可以轮流吧2的空位补全,如下。
a为4,b为4,c为2
时间: 1 2 3 4
亮灯: c a c b
时间: 5 6 7 8
亮灯: c a c b
...
实际上这题都不用想,直接写个暴力程序,把小于5的情况都遍历一下,就知道所有解了。
(为什么小于5?,这不是就给你3个数字吗,保险点也可以跑大点,想想就知道不可能很大。)
AC代码
int main() {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if ((a==1)||(b==1)||(c==1)
||(a == 2 && b == 2)
|| (a == 2 && c == 2)
|| (c == 2 && b == 2)
|| (a == 3 && b == 3 && c == 3)
|| (a == 2 && b == 4 && c == 4)
|| (a == 4 && b == 2 && c == 4)
|| (a == 4 && b == 4 && c == 2)
)
printf("YES\n");
else printf("NO\n");
return 0;
}
D Anton and Chess CodeForces - 734D
题意
Bishop 可以斜着走,Rook 可以垂直水平走,Queen 可以斜着走也可以垂直水平走。
但是不能越过其他棋子。求能否吃掉对方的 King。
题解
枚举 King 的八个方向的第一个棋子,看是否是能吃掉它的。
根据 King 的坐标把其他棋子分类,然后根据里 King 的距离排序,再判断。
AC代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<map>
#include<vector>
#define s(x,y) sort(x.begin(),x.end(),y);
using namespace std;
typedef long long ll;
const ll N=5e5+5;
struct node{
char c;
int x,y;
}s[N];
vector<node>l,r,d,u,ld,lu,rd,ru;
bool cmpl(node a,node b){
return a.y>b.y;
}
bool cmpr(node a,node b){
return a.y<b.y;
}
bool cmpu(node a,node b){
return a.x>b.x;
}
bool cmpd(node a,node b){
return a.x<b.x;
}
bool cmpru(node a,node b){
return a.x<b.x;
}
bool cmpld(node a,node b){
return a.x>b.x;
}
bool cmprd(node a,node b){
return a.x<b.x;
}
bool cmplu(node a,node b){
return a.x>b.x;
}
int main(){
int n,x,y,a,b;
char c;
int flag=0;
scanf("%d%d%d",&n,&x,&y);
for(int i=0;i<n;i++){
node tmp;
scanf("%s%d%d",&tmp.c,&tmp.x,&tmp.y);
if(tmp.x==x){
if(tmp.y>y)r.push_back(tmp);
else l.push_back(tmp);
}
else if(tmp.y==y){
if(tmp.x>x)d.push_back(tmp);
else u.push_back(tmp);
}
else if(tmp.x+tmp.y==x+y){
if(tmp.x>x)rd.push_back(tmp);
else lu.push_back(tmp);
}
else if(abs(tmp.x-tmp.y)==abs(x-y)){
if(tmp.x>x)rd.push_back(tmp);
else lu.push_back(tmp);
}
}
s(l,cmpl);
s(r,cmpr);
s(d,cmpd);
s(u,cmpu);
s(lu,cmplu);
s(ld,cmpld);
s(ru,cmpru);
s(rd,cmprd);
if(l.size()&&(l[0].c=='R'||l[0].c=='Q'))flag=1;
if(r.size()&&(r[0].c=='R'||r[0].c=='Q'))flag=1;
if(u.size()&&(u[0].c=='R'||u[0].c=='Q'))flag=1;
if(d.size()&&(d[0].c=='R'||d[0].c=='Q'))flag=1;
if(lu.size()&&(lu[0].c=='B'||lu[0].c=='Q'))flag=1;
if(ld.size()&&(ld[0].c=='B'||ld[0].c=='Q'))flag=1;
if(ru.size()&&(ru[0].c=='B'||ru[0].c=='Q'))flag=1;
if(rd.size()&&(rd[0].c=='B'||rd[0].c=='Q'))flag=1;
if(flag)printf("YES\n");
else printf("NO\n");
scanf(" ");
}
E Misha and Forest CodeForces - 501C
题意
定义一个森林为非有向无环图(没有重边与环)给出\(n(n\le2^{16})\)个节点的两个信息,第一个为该节点有几个与其相邻的节点,第二个为与其相邻的节点的编号的异或值。输出森林有几条边与这些边的具体信息。(可以以任意顺序输出,数据保证有解)
tips:节点从0开始编号
题解
既然是一个非有向无环图,那必存在至少 2 个节点只有一条边与其相连,所以我们可以先找出这些点,然后记录下这些边,把点删除,这时我们得到了一张新的图,这新图中又会出现只有一条边与其相连的点(就是之前只有两条边相连的点),以此往复,直到没有新的点出现。
(还有没有连边的点,但我们要求的是边,所以可以完全不管那些点)
AC代码
int deg[N],s[N];
int l[N];
int a[N],b[N];
vector<int>e[N];
int main(){
int n,m=0;
int cnt=0;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d%d",°[i],&s[i]);
if(deg[i]==1){
l[cnt++]=i;;
}
}
for(int i=0;i<cnt;i++){
int x=s[l[i]];
if(deg[l[i]]!=1)continue;
a[m]=l[i];
b[m++]=x;
deg[x]--;
s[x]^=l[i];
if(deg[x]==1){
l[cnt++]=x;
}
}
printf("%d\n",m);
for(int i=0;i<m;i++){
printf("%d %d\n",a[i],b[i]);
}
return 0;
}
F Police Recruits CodeForces - 427A
题意
输入 n 个整数。如果整数为 -1,则表示发生了犯罪。如果整数为正数,则这个数表示招募的警官人数。一次最多招募 10 名警官。
一个警察只能解决一个案件,如果犯罪出现的时候,没有警察,那那个犯罪对应的案件就不会被解决。
问,未处理的犯罪数量。
题解
简单模拟
AC代码
int main() {
int n, x,sum=0,ans=0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
if (x == -1) {
if (sum == 0)ans++;
else sum--;
}
else sum+=x;
}
printf("%d\n", ans);
return 0;
}
G Suffix Three CodeForces - 1281A
题意
给定 n 个字符串,对于每个字符串:
- 若它以 po 结尾,输出 FILIPINO
- 若它以 desu 或 masu 结尾,输出 JAPANESE
- 若它以 mnida 结尾,输出 KOREAN
保证所给的字符串一定满足以上的三种情况之一。
题解
简单模拟
AC代码
int n;
char s[1003];
int main(){
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
if(n>=2
&& s[n-1]=='p'
&& s[n]=='o')
printf("FILIPINO");
else if(n>=5
&& s[n-4]=='m'
&& s[n-3]=='n'
&& s[n-2]=='i'
&& s[n-1]=='d'
&& s[n]=='a')
printf("KOREAN");
else printf("JAPANESE");
}
return 0;
}
H 可乐 黑暗爆炸 - 4887
题意
中文题,应该不会读错吧?
题解
正解矩阵快速幂优化dp,我不会(我不是dp选手,想学的可以问你们璐聃师兄。
我乱搞出来的。(可能算dp吧
我们可以把所有的情况看成一颗树,根为1,爆炸为0,对应的节点是对应的数字,树深度是时间,然后就可以构造出一颗状态树,我们要求的实际上是树的叶节点数。
所以我们只要遍历一遍这颗树就能知道答案了。但那样复杂度太高了,所以我们可以想办法化简归并一些操作。
仔细观察就会发现,某个数字节点的分支是相同的,比如样例中,节点1的分支固定为3个,0,1,2,即自爆,不动,去其他节点。节点2的分支为4个,0,1,2,3,他多了一个去3号节点。所以节点的分支等于,2+边数,这样我们只要知道这个点是什么节点就可以知道他有多少个分支了。
在同一深度(时间)中的所有同数值节点实际上完全没有区别,我们不用一个个去计算他,我们可以和在一起计算,比如样例中,在第二个时刻,总共有2种操作正在1上,2种在2上,一种在3上。因为,第一步,1到2,所以,这时1有1个,2有一个,第二步,1再到2,2到3和1,这时,1就有2个,2有2个,3有1个。
我们知道了节点1有2个,节点2有2个,节点3有一个,根据上面的推论,我们就知道,下一层总共有,23+24+3=17 个节点。
总体逻辑就是上面那边,一步一步去扩散状态的数量。然后计数。
AC代码
const int mod=2017;
int en[31];//点的边数
int tmp[31];//临时数组,防止加状态数的影响
int sta[31];//状态数,即这个时间在这个节点上的状态的数量
int e[31][100];
int s[31];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
e[u][s[u]++] = v;
e[v][s[v]++] = u;
en[u]++;
en[v]++;
}
sta[1] = 1;
int t;
scanf("%d", &t);
int ans = 0;
for (int i = 0; i < t - 1; i++) {
for (int j = 1; j <= n; j++) {
ans = (ans + sta[j] ) % mod;//每一个节点都有爆炸选项,所以ans要加上所有节点数
for (int k = 0; k < s[j]; k++) {
int x = e[j][k];
tmp[x] = (tmp[x] + sta[j]) % mod;//节点扩散
}
}
for (int j = 1; j <= n; j++) {
sta[j] = (sta[j] + tmp[j]) % mod;
tmp[j] = 0;
}
}
for (int i = 1; i <= n; i++) {
ans = (ans + (en[i] + 2) % mod*sta[i] % mod) % mod;
}
printf("%d\n", ans);
return 0;
}
I Odd Divisor CodeForces - 1475A
题意
给你一个整数n。检查n是否有一个大于1的奇数除数(是否存在这样一个数x(x>1),n可以被x整除,x是奇数)。
题解
检查是不是2的幂
AC代码
//方法一
int main() {
ll n,t;
scanf("%lld", &t);
while (t--) {
int flag = 0;
scanf("%lld", &n);
while (n % 2 == 0)n /= 2;
printf("%s\n",n!=1 ? "YES" : "NO");
}
return 0;
}
//方法二
int main() {
ll n,t;
scanf("%lld", &t);
while (t--) {
int flag = 0;
scanf("%lld", &n);
printf("%s\n",n&(n-1)? "YES" : "NO");
}
return 0;
}