Codeforces Round #838 (Div. 2)
创始人
2024-04-25 09:30:51
0

A. Divide and Conquer

题目链接:Problem - A - Codeforces

样例输入:

4
4
1 1 1 1
2
7 4
3
1 2 4
1
15

样例输出:

0
2
1
4

题意:一个数组是好的当且仅当所有的元素和是一个偶数,现在给我们一个初始数组,我们可以对这个数组进行操作,每次操作可以选择一个数并将其缩小为原来的一半,注意是下取整,现在问我们至少需要操作多少次才能把给定数组变为好的。

分析:容易发现,一个数组的元素和要么是奇数要么是偶数,如果一开始是偶数的话那么我们呢就没有必要进行操作,否则我们必须要改变一个数的奇偶性才能把数组变为好的,因为任意两个数之间是不会相互影响的,所以我们不需要同时对两个数进行操作,我们只需要遍历一遍所有的数,记录每一个数改变奇偶性所需要的最少操作次数,那么答案就是所有最小操作次数中的最小值。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=53;
int a[N];
int main()
{int T;cin>>T;while(T--){int mi=0x3f3f3f3f;int ans=0;int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);ans+=a[i];int cnt=1;while((a[i]&1)==((a[i]/2)&1)) cnt++,a[i]/=2;mi=min(mi,cnt);}if(ans&1) printf("%d\n",mi);else puts("0");}return 0;
}

B. Make Array Good

题目链接:Problem - B - Codeforces

样例输入: 

4
4
2 3 5 5
2
4 8
5
3 4 343 5 6
3
31 5 17

样例输出:

4
1 2
1 1
2 2
3 0
0
5
1 3
1 4
2 1
5 4
3 7
3
1 29
2 5
3 3

题意:一个数组是好的当且仅当这个数组中的任意两个数a和b都存在整除关系,即a|b或者b|a成立,现在我们需要对数组进行操作,每次操作可以把一个数a加上x,x是一个不大于a的非负整数,现在让我们进行不超过n次的操作使得数组变为好的,输出一种可行方案。

分析:我们可以考虑把所有数都变为2的幂次,这样的话一定有任意两个数之间存在整除关系,而且我们可以发现,对于任意一个数a,都会有一个x是2的幂次且满足a<=x<=2*a,那么我们只需要让a加上x-a即可将a变为大于等于a的第一个2的幂次,这样的操作次数一定是n,满足题意。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=1e5+10;
int a[N],x[N];
vectorp;
int main()
{int T;cin>>T;long long t=1;while(t<=2e9) p.push_back(t),t<<=1;while(T--){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);x[i]=*lower_bound(p.begin(),p.end(),a[i])-a[i];}printf("%d\n",n);for(int i=1;i<=n;i++)printf("%d %d\n",i,x[i]);}return 0;
}

C. Binary Strings are Fun

题目链接:Problem - C - Codeforces

样例输入: 

6
1
1
1
0
2
11
3
010
9
101101111
37
1011011111011010000011011111111011111

样例输出:

1
1
3
3
21
365

题意:一个奇数长度的01串b是好串需要满足以下条件:

对于任意一个奇数下标idx:

如果b[idx]=1,那么前idx个位置中1的个数要大于0的个数

如果b[idx]=0,那么前idx个位置中0的个数要大于1的个数。

对于一个长度为k的01串a,扩展后的01串长度就为2*k-1,假设扩展后的01串为c,那么就满足a[i]=c[2*i-1]

现在给定一个长度为n的01串a,求a的扩展串中是好串的数量,对998244353取模。

分析:设f[i]表示前i位扩展后占多数的情况数,g[i]表示前i位扩展后只多一个的方案数

那么我们现在来分析第i位的更新:

初始条件肯定有f[1]=g[1]=1

如果第i位和第i-1位是相同的,那么扩展后我们多出来需要自己填写的那一位可以填1也可以填0,这个时候就有f[i]=f[i]*2,要想保证前i位扩展后占多数的那一位比占少数的那一位只多1,那么需要在前i-1位扩展后占多数的那一位比占少数的那一位只多1的基础上在新扩展的那一位上填上与这一位相反的数字即可,即g[i]=g[i-1].

如果第i位和第i-1位是相反的,那么扩展后我们多出来需要自己填写的那一位一定要填与第i位相同的数字,而且要保证前i-1位扩展的扩展后占多数的那一位比占少数的那一位只多1,这样的话才能够实现前i位扩展后第i位上的数字出现次数多,而且只会多1,所以有f[i]=g[i],而且g[i]=g[i-1].

通过这样我们就能递推出最终的结果了。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=2e5+10,mod=998244353;
int f[N];//f[i]表示前i位扩展后占多数的情况数
int g[N];//g[i]表示前i位扩展后只多一个的方案数 
char s[N];
int main()
{int T;cin>>T;while(T--){int n;scanf("%d%s",&n,s+1);f[1]=g[1]=1;int ans=1;for(int i=2;i<=n;i++){if(s[i]!=s[i-1]) g[i]=g[i-1],f[i]=g[i];else f[i]=f[i-1]*2%mod,g[i]=g[i-1];ans=(ans+f[i])%mod; }printf("%d\n",ans);}return 0;
}

但是我们发现无论相邻两位是否相同都有g[i]=g[i-1],又因为一开始g[1]=1,那么可以知道所有的g数组都是1,所以我们可以省掉g数组了

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=2e5+10,mod=998244353;
int f[N];//f[i]表示前i位扩展后占多数的情况数
char s[N];
int main()
{int T;cin>>T;while(T--){int n;scanf("%d%s",&n,s+1);f[1]=1;int ans=1;for(int i=2;i<=n;i++){if(s[i]!=s[i-1]) f[i]=1;else f[i]=f[i-1]*2%mod;ans=(ans+f[i])%mod; }printf("%d\n",ans);}return 0;
}

D. GCD Queries

题目链接:Problem - D - Codeforces

样例输入: 

2
211
5241

样例输出:

? 1 2! 1 2? 1 2? 2 3! 3 3

题意:给定一个长度为n的排列0~n-1,现在让我们找出来0的下标,只需要找出来两个数i,j,只要满足其中一个数是0的下标即可。每次我们可以给出两个不同的下标i和j进行询问,电脑会反馈pi和pj的最大公约数,其中定义0与a的最大公约数就是a。让我们在询问次数不超过2*n的情况下找到满足题目条件的i和j。

分析:我们首先固定一个数,让这个数与其余的n-1个数进行询问,这样我们就可以唯一的确定第一个数的值。

比如说第一次我们固定的数是2,那么我们用2与其余所有数进行询问,那么询问的答案最大就是2,因为任何一个数与2的最大公约数不可能比2还大,所以第一个数就是2,同理可以得到其他数也是一样的。这样我们通过一次操作就可以得到第一个固定的数是2.我们已经知道了其余所有数与2的最大公约数是几了,对于最大公约数不是2的数我们就可以直接排除了,因为0与2的最大公约数是2,所以通过这一次操作我们就将n个数变为了n/2个数,花费了n次(注意这里确切是n-1次操作,不过为了方便书写方便就写n了,由于n>n-1,所以如果n可行那么n-1更可行)操作。下一次我们就至少可以将n/2个数变为n/4个数,花费的操作次数就是n/2,依次类推就有n+n/2+n/4+……<2*n的,所以这个方法是可行的,最后只剩下两个数时直接退出即可

如果我们第一个数是k,那么n次操作就可以将n个数变为n/k个数,那么这样最后花费的次数会更少,所以我们不必担心次数的问题。

但是我们忽略了一个问题,就是当第一个数是1的时候,我们花费了n次操作,结果全部是1,那么我们这n次操作就相当于只排除了第一个数。所以我们需要额外注意一下这种情况,我们让第一个数与第二个数进行一次询问,再让第一个数与第三个数进行一次询问,如果两次答案是一样的话那么说明第一个数不是0,我们直接把第一个数清空即可,如果两次答案不一样虽然不能说明第一个数一定是0,但是可以说明第一个数一定不是1,因为1与任何数询问得到的结果都是相同的,那么根据上面的分析我们可以发现询问次数一定是够用的。我们通过两次操作排除了一个数,就相当于原来问题是在2*n次询问中从n个数中找,现在变为了在(2*n-2)次询问中从n-1个数中找,这是等价的,只是问题规模变小了而已。

代码中是从最后一个数开始往前询问的。

因为每次询问完都会保留第一个数,所以每询问完一轮我都会对所有的数进行一次反转,这样上次固定的数一定会在这次被删除

注意这个细节。

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{int T;cin>>T;while(T--){int n;scanf("%d",&n);vectorp;for(int i=1;i<=n;i++) p.push_back(i);int x;while(p.size()>2){reverse(p.begin(),p.end());vector t;t.push_back(p[p.size()-1]);int mx=0;for(int i=0;i<=1;i++){printf("? %d %d\n",p[p.size()-1],p[i]);fflush(stdout);scanf("%d",&x);if(x>mx){mx=x;t.clear();t.push_back(p[p.size()-1]);t.push_back(p[i]);}else if(x==mx)t.push_back(p[i]);}if(t.size()==3)//说明最后一个数肯定不是0,主要是用于排除最后一个数是1的情况{p.pop_back();continue;}for(int i=2;imx){mx=x;t.clear();t.push_back(p[p.size()-1]);t.push_back(p[i]);}else if(x==mx)t.push_back(p[i]);}p=t;}printf("! %d %d\n",p[0],p[1]);fflush(stdout);scanf("%d",&x);}return 0;
}

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...