前言
这是一个蒟蒻的比赛心得与代码,请各位dalao不要嫌弃。
本赛涉及题目:
A: [Odds and Ends]
T1 Odds and Ends
题面
原题地址: [A]
题意
问能否将数组分割成奇数个长度为奇数,头尾都为奇数的集合。
思路
(2*a+1)*(2*k+1)=2*p+1—->>n必为奇数;
此时母串就是一个集合,只要满足头尾都为奇数就行了。
【易证头或尾为偶数时必不能完成题面】
代码
#include<bits/stdc++.h>
using namespace std;
int i,n,x;
int main()
{
scanf("%d",&n);
if(!n&1)//n的奇偶性;
{
printf("No");
return 0;
}
for(i=1;i<=n;i++)
{
scanf("%d",&x);
if(i==1||i==n)//头尾的奇偶性;
{
if(!x&1)
{
printf("No");
return 0;
}
}
}
printf("Yes");
return 0;
}
小结
水题*1
T2 Tell Your World
题面
原题地址: [B]
题意
能否找到两条平行线使所有点都在这两条线上。
思路
找平行线的斜率,枚举点。
斜率k有n*n种可能,枚举点再*n,绝对TLE。
此时我们发现,如果斜率k不是(1,2),(2,3),(1,3)的斜率,这个k必定不能满足题面(自己证)。
于是我们只用枚举三个k。时间复杂度O(n)过。
代码
#include<bits/stdc++.h>
using namespace std;
int s[3000],n,p,i;
bool if_line(double k)//寻找这个k是否能行;
{
p=-1;
for(i=2;i<=n;i++)
{
if(s[i]-s[1]!=k*(i-1))//第一条线不满足;
{
if(p==-1) p=i;
else
{
if(s[i]-s[p]!=k*(i-p)) return false; //第二条平行线的构造;
}
}
}
return p!=-1;
}
int main()
{
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&s[i]);
if_line(s[2]-s[1])||if_line((s[3]-s[1])*0.5)||if_line(s[3]-s[2])?printf("Yes"):printf("No");
}
小结
要枚的三个k手算得我一脸懵逼(可惜这是关键)
T3 From Y to Y
题面
原题地址: [C]
题意
有一个串,将这个串分成长度为1的n个子串,再将这些子串合并,合并成母串的最小代价k。
代价:两个合并子串s,t中重复字母的总个数。给出k,求原串(有多种可能,只输出一种)。
思路
同种字母合并才会产生代价,于是我们可以先将同种字母合并,再合并由同种字母组成的字符串。
此时再通过计算发现无论什么合并方式,代价都是l*(l-1)/2。
枚’a’…’z’的l,使总代价=k。
代码
#include<bits/stdc++.h>
using namespace std;
int cur,k,n,i;
int main()
{
scanf("%d",&n);n*=2;
if(n==0)//特判(本代码对0没有任何抵抗力);
{
printf("a");
return 0;
}
while(n)
{
k=1;
while(k*(k+1)<=n) k++;//第cur+1个字母的个数;
for(n-=k*(k-1),i=1;i<=k;i++) printf("%c",cur+'a');
cur++;//下一个字母;
}
return 0;
}
小结
代价恒为最小值是重点!!!
T4 Harmony Analysis
题面
原题地址: [D]
题意
每两行保证有n组相同点(竖着的两字符一样即为相同),每行仅由’+’与’*’组成;
思路
第k个图形由第k-1个图形向下,向右复制一份,向右下复制一遍反的(’+”*’互逆)。
手动画图推。。。(如果有dalao知道数学证明,请告知一下!!)。
代码
#include<bits/stdc++.h>
#define M 1<<9
using namespace std;
char t[M][M];
int l=1,n,e,i,j;
int main()
{
scanf("%d",&n);t[0][0]='+';
for(e=1;e<=n;e++)//递推次数;
{
for(i=0;i<l;i++)
{
for(j=0;j<l;j++)
{
t[i+l][j]=t[i][j+l]=t[i][j];//等位复制;
t[i+l][j+l]=t[i][j]=='+'?'*':'+';//反复制;
}
}
l*=2;
}
for(i=0;i<1<<n;i++)
{
for(j=0;j<1<<n;j++)
{
printf("%c",t[i][j]);
}
printf("\n");
}
return 0;
}
小结
手蒙推递推式花了不少时间!!!
T5 Bear and Prime Numbers
题面
原题地址: [E]
Ps:
其他题基本就是一遍A,但这道题却交了不少次啊!!!
题意
有n个数a[i]..a[n],有m个询问,询问[li,ri]中f(p)的总和(p<-[li,ri])。
定义f(p)(p为素数)=(n个数中有多少个数整除p)。
思路
直接以询问进行暴力,绝对TLE,所以必定需要优化:我们发现询问很多,可以把各种f(p)先算出来(埃氏筛),再在区间内直接算,但这还不够,还是TLE,还可以用前缀和进行优化,然后就AC了。。。
WA的过程
1.MLE
li,ri<=2*10^9,但数组开不到10^9(本地就炸),10^8(MLE);然而用到的最大质数是min(ri,a[i]),而a[i]max=10^7;所以数组开到10^7就行了。
2.RE
a[i]<=10^7,但li,ri<=2*10^9,这样就会在算区间时莫名其妙访问错误地址,然后RE。
3.TLE
素数筛法
常规质数筛法为O(n^2),马上就TLE了,然后就可以改成埃氏筛O(n log^2 n),或线性筛(我也不知道叫什么)。
此时选用埃氏筛是因为这种筛法会遍历一遍素数p的倍数,与题意相符。
for(i=2;i<=MaxN;i++) { if(!if_prime[i]) { prime[cur]=i; cur++; for(j=i+i;j<=MaxN;j++) if_prime[j]=true;//遍历i(素数)的倍数; } }
于是可以改成,下文中的样子。
f(p)的合并
原本想从li到ri for一遍,不过TLE了,再看数据范围m<=50000,于是就可以用前缀和维护,预处理费时,但后来都是O(1)求answer。
代码
#include<bits/stdc++.h>
#define maxn 10000005
using namespace std;
int t[10000007],n,i,j,x,y,ans,m,sum[10000007];
bool p[10000007];
int main()
{
scanf("%d",&n);
while(n--)
{
scanf("%d",&x);
t[x]++;
}
for(i=2;i<=maxn;i++)//埃氏筛;
{
if(!p[i])
{
sum[i]=t[i];
for(j=i+i;j<=maxn;j+=i)
{
p[j]=true;
if(t[j]) sum[i]+=t[j];//小优化:直接推f(p);
}
}
}
for(i=1;i<=maxn;i++)//维护前缀和;
{
sum[i]+=sum[i-1];
}
scanf("%d",&m);
while(m--)
{
ans=0;
scanf("%d%d",&x,&y);
if(x!=maxn) x=min(x,maxn-1);y=min(y,maxn);//防止数组下标越界;
ans=sum[y]-sum[x-1];
printf("%d\n",ans);
}
return 0;
}
小结
题面很重要,数据范围很重要,算法更重要!!!
总结
这次比赛还差最后一道题就AK了,可惜啊,还有就是D题手推和B题手推花了大部分时间,什么时候要好好去看看数学了。。。(这一次的比赛还是比较水的嘛QWQ)。