文章目录
这应该是站内相对详细的解题报告吧。
转载思路请@本人。
感想
关于自己
这一次感觉题目变水了(对比第五期来说,只是个人感觉)
其实本来想着可能人均满分来划水的,没想到emm……排名:2
上一期由于和CSP冲突,所以错过了比赛
CSP的比赛时间恰好把第六期比赛时间包住了QAQ
然后捏,由于自己在第二题思考的时间过长,导致错失第一,哎
第八期要开始了,希望自己能考得好一些吧
毕竟就在CSP复赛后一天(
R
P
+
+
RP++
RP++)
关于平台
这一次倒是没有出什么错误了
觉得限制入场时间做得很好,不过建议一点:
排名最后出!
因为有些人可能根据排名来控制自己的分数和时间(这很让我无语。。)
这次也没有硬性要求发在特殊地方了,只要发了博客就行了
顺便提一句:每日一题上的难度偏差也太大了吧,困难题随便切,简单题卡死不会
建议工作人员能抽出时间好好整理一下难度标签
第一题 奇偶排序(难度:签到)
题目描述
给定一个存放整数的数组,重新排列数组使得数组左边为奇数,右边为偶数。
100分做法
其实思路非常简单,
先输出奇数,后输出偶数就行啦(根本没有什么排序)。
C
+
+
C++
C++代码如下:
#include<cstdio>
int a[100005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)if(a[i]%2!=0)printf("%d ",a[i]);
for(int i=1;i<=n;i++)if(a[i]%2==0)printf("%d ",a[i]);
}
第二题 小艺照镜子(难度:入门)
题目描述
求已知字符串的最长回文子串。
100分做法1
看到回文串,我们可以想到马拉车、字符串哈希……随便选一种来做就切了。
100分做法2
发现
n
≤
10000
n\leq10000
n≤10000
然后,暴力就可以过!
于是我们枚举中间点,然后暴力往两边拓展,
拓展到两边都不相同后停止,计算答案,
最后输出最大值即可。
注意!
- 奇数串和偶数串分开处理;
- C + + C++ C++读入很EX,我是用 g e t c h a r ( ) getchar() getchar()读入的, s c a n f scanf scanf和 c i n cin cin好像都不行。
C + + C++ C++代码如下:
#include<bits/stdc++.h>
using namespace std;
char s[100015];
int main()
{
int n=0,ans=1;
char ch;
while((ch=getchar())!=EOF)s[++n]=ch;//如果有东西就继续读入
for(int i=1;i<=n;i++)
{
int l=i,r=i;
while(s[l-1]==s[r+1]&&l>1&&r<=n)l--,r++;//奇数
ans=max(ans,r-l+1);
l=i;r=i+1;
if(s[l]!=s[r])continue;
while(s[l-1]==s[r+1]&&l>1&&r<=n)l--,r++;//偶数
ans=max(ans,r-l+1);
}
printf("%d\n",ans);
}
第三题 交换后的or(难度:入门~中等)
题目描述
给定两组长度为 n n n的二进制串,请问有多少种方法在第一个串中交换两个不同位置上的数字,使得这两个二进制串“或”的结果发生改变?
暴力做法
枚举两个位置,然后交换,计算出是否改变,然后统计答案。
但是这样时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)的,无法通过本题。
100分做法
考虑到交换只和修改的两个位置有关,而一个位置只有四种情况,
于是我们记录出当前每种情况各出现了多少次(如01,10等),
然后用手算出哪两种情况可以使结果发生改变,
接着统计答案即可。
注意开
l
o
n
g
long
long
l
o
n
g
long
long。
C
+
+
C++
C++代码如下:
#include<bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
int n;
long long ans,s1,s2,s3,s4;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%1d",&a[i]);
for(int i=1;i<=n;i++)scanf("%1d",&b[i]);
for(int i=1;i<=n;i++)
{
if(a[i]==0&&b[i]==0)
{
ans+=s3+s4;
s1++;
}
if(a[i]==0&&b[i]==1)
{
ans+=s3;
s2++;
}
if(a[i]==1&&b[i]==0)
{
ans+=s1+s2;
s3++;
}
if(a[i]==1&&b[i]==1)
{
ans+=s1;
s4++;
}
}
printf("%lld",ans);
}
第四题 去除整数(难度:入门~中等)
题目描述
已知存在集合 A A A包含 n n n个整数,从 1 1 1到 n n n。 存在 m m m个整数 a 1 , a 2 , . . . , a m a_1,a_2,...,a_m a1,a2,...,am。 在集合 A A A中去除这 m m m个整数的的倍数。 输出集合中包含的元素的个数。
100分做法1
发现
m
≤
10
m\leq10
m≤10,
于是直接搜索!
枚举当前选哪些数,然后求最小公倍数
l
c
m
lcm
lcm,
如果有奇数个数,就减去
n
÷
l
c
m
n\div lcm
n÷lcm,否则加上
n
÷
l
c
m
n\div lcm
n÷lcm。
最后输出答案就行了。
什么?你怎么知道这是对的?
这是容斥的板题,如果不会容斥可以上百度学习。
100分做法2
这是一种规范的容斥做法,不过时间复杂度和搜索一样。
枚举子集,然后计算出子集中的数的
l
c
m
lcm
lcm和子集中数的个数,
接着按照做法1加加减减,
最后输出答案即可。
C
+
+
C++
C++代码如下:
#include<bits/stdc++.h>
using namespace std;
long long a[15]={};
long long gcd(long long x,long long y){return y==0?x:gcd(y,x%y);}
long long lcm(long long x,long long y){return x*y/gcd(x,y);}
int main()
{
int m;
long long n,tot;
scanf("%lld%d",&n,&m);
tot=n;
for(int i=1;i<=m;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<(1<<m);i++)//枚举非空子集
{
long long g=-1,t=1;
for(int j=1;j<=m;j++)
{
if((i&(1<<(j-1)))>0)
{
if(g==-1)g=a[j];
else g=lcm(g,a[j]);
if(g>n)break;
t*=-1;
}
}
if(g!=-1)tot+=t*(n/g);
}
printf("%lld\n",tot);
}