入门赛2

来源:互联网 发布:腰部按摩器 知乎 编辑:程序博客网 时间:2024/06/05 10:46

  • 前言
  • T1 Odds and Ends
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T2 Tell Your World
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T3 From Y to Y
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T4 Harmony Analysis
    • 题面
    • 题意
    • 思路
    • 代码
    • 小结
  • T5 Bear and Prime Numbers
    • 题面
    • Ps
    • 题意
    • 思路
    • WA的过程
      • MLE
      • RE
      • TLE
        • 素数筛法
        • fp的合并
    • 代码
    • 小结
  • 总结

前言

这是一个蒟蒻的比赛心得与代码,请各位dalao不要嫌弃。

本赛涉及题目:
A: [Odds and Ends]

B: [Tell Your World]

C: [From Y to Y]

D: [Harmony Analysis]

E: [Bear and Prime Numbers]


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<<9using 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 10000005using 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)。