• Contest1585


    Contest1585 - 2018-2019赛季多校联合新生训练赛第一场

    C 10187 查找特定的合数

    D 10188 传话游戏

    H 10192 扫雷游戏

    C 传送门

    题干:

    题目描述
        自然数中除了能被1和本身整除外,还能被其他数整除的数叫合数。每个合数都可以写成几个质数相乘的形式,这几个质数都叫做这个合数的质因数。比如8=2×2×2,2就是8的质因数。在1—N(N≤200000)按从小到大顺序排列的自然数序列中,查找第M个有X(2≤X≤6)个不同质因数的合数。例如,第3个有2个不同质因数的合数是12(12只有2、3两个不同的质因数,在12之前有2个不同质因数的合数分别为6和10)。
    
    输入
    共1行,分别为M,X。
    
    输出
    共1行,为第M个有X个不同质因数的合数。
    
    样例输入
    3 2
    
    样例输出
    12
    View Code

    题解:

      步骤:

        (1):用线性筛素数(欧拉筛法)在O(n)的时间内预处理出[1,2e5]间所有的质数。

        (2):从1到2e5开始枚举,对于数 i 找到其含有的质因子的个数,用tot[ ]数组存储。

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 using namespace std;
     6 #define mem(a,b) memset(a,b,sizeof(a))
     7 const int maxn=2e5+10;
     8 
     9 int M,X;
    10 int tot[maxn];//tot[i] : i 含有的质因子个数
    11 //===============线性筛素数(欧拉筛法)=====================
    12 int vis[maxn];
    13 int prime[maxn];
    14 int cnt=0;//cnt用来计数,prime数组保存素数
    15 void isprime(int n)
    16 {
    17     for(int i=2;i<=n;i++)
    18     {
    19         if(!vis[i])
    20             prime[cnt++]=i;//如果未被标记过,则表示为素数
    21         for(int j=0;j<cnt && i*prime[j]<=n;j++)//当标记的合数超出范围则退出
    22         {
    23             vis[i*prime[j]]=1;
    24             if(i%prime[j] == 0)
    25                 break;//关键步骤
    26         }
    27     }
    28 }
    29 //===========================================================
    30 bool Check(int x)//二分检查 x 是否为素数
    31 {
    32     int l=-1,r=cnt+1;
    33     while(r-l > 1)
    34     {
    35         int mid=l+((r-l)>>1);
    36         if(prime[mid] == x)
    37             return true;
    38         if(prime[mid] > x)
    39             r=mid;
    40         else
    41             l=mid;
    42     }
    43     return false;
    44 }
    45 void Updata(int num)
    46 {
    47     int x=sqrt(num);
    48     for(int i=2;i <= x;++i)
    49     {
    50         if(num%i != 0 || i == num)
    51             continue;
    52         int j=num/i;
    53         tot[num]=tot[num]+(Check(i) ? 1:0)+(i != j && Check(j) ? 1:0);
    54     }
    55 }
    56 int Solve()
    57 {
    58     mem(vis,0);
    59     mem(tot,0);
    60     isprime(2e5);
    61     for(int i=2;i <= 200000;++i)
    62         Updata(i);//更新tot[i]
    63     for(int i=2;i <= 200000;++i)
    64     {
    65         if(tot[i] == X)//查找第 M 个只有 X 个质因子的数
    66             M--;
    67         if(M == 0)
    68             return i;
    69     }
    70 }
    71 int main()
    72 {
    73     scanf("%d%d",&M,&X);
    74     printf("%d
    ",Solve());
    75 }
    View Code

      时间复杂度分析:

        (1):预处理线性筛 : O(n),n = 2e5

        (2):枚举 : O( n*√n * log(cnt) ),(n = 2e5,cnt = 17984);

      所以总的时间复杂度为 O( n*√n * log(cnt) );

    D 传送门

    题干:

    题目描述
    有这样一个朋友网络,如果a认识b,那么a收到某个消息,就会把这个消息传给b,以及所有a认识的人。但是,请你注意,如果a认识b,b不一定认识a。现在我们把所有人从1到n编号,给出所有“认识”关系,问如果i发布一条新消息,那么会不会经过若干次传话后,这个消息传回给了i(1≤i≤n)。
    
    输入
    第1行是两个数n(n<1000)和m(m<10000),两数之间有一个空格,表示人数和认识关系数。接下来的m行,每行两个数a和b,表示a认识b(1≤a,b≤n)。认识关系可能会重复给出,但1行的两个数不会相同。
    
    
    输出
    一共有n行,每行一个字符T或F。第i行如果是T,表示i发出一条新消息会传回给i;如果是F,表示i发出一条新消息不会传回给i。
    
    
    样例输入
    4 6
    1 2
    2 3
    4 1
    3 1
    1 3
    2 3
    
    
    样例输出
    T
    T
    T
    F
    View Code

    题解:

      考察知识点:强连通分量分解

      以人作为顶点,人与人之间的认识关系作为边建立一个有向图。

      通过SCC判断某人a是否与其他人构成强连通分量,如果构成,那么此人可以通过他人得到自己传出去的消息,反之,将得不到。

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<vector>
     5 #include<map>
     6 #include<algorithm>
     7 using namespace std;
     8 #define mem(a,b) memset(a,b,sizeof(a))
     9 #define pb(x) push_back(x)
    10 const int maxn=1e3+50;
    11 
    12 int n,m;
    13 bool vis[maxn];//访问标记
    14 int color[maxn];//所属强连通分量的编号
    15 int tot[maxn];//记录强连通分量的编号出现的次数
    16 vector<int >G[maxn],rG[maxn];//图,反向图
    17 vector<int >vs;
    18 void addEdge(int u,int v)
    19 {
    20     G[u].pb(v);
    21     rG[v].pb(u);
    22 }
    23 void Dfs(int u)
    24 {
    25     vis[u]=true;
    26     for(int i=0;i < G[u].size();++i)
    27     {
    28         int to=G[u][i];
    29         if(!vis[to])
    30             Dfs(to);
    31     }
    32     vs.pb(u);
    33 }
    34 void rDfs(int u,int k)
    35 {
    36     vis[u]=true;
    37     color[u]=k;
    38     tot[k]++;
    39     for(int i=0;i < rG[u].size();++i)
    40     {
    41         int to=rG[u][i];
    42         if(!vis[to])
    43             rDfs(to,k);
    44     }
    45 }
    46 void Solve()
    47 {
    48     mem(vis,false);
    49     for(int i=1;i <= n;++i)
    50         if(!vis[i])
    51             Dfs(i);
    52     mem(vis,false);
    53     mem(tot,0);
    54     int k=0;//强连通分量编号
    55     for(int i=vs.size()-1;i >= 0;--i)
    56     {
    57         int u=vs[i];
    58         if(!vis[u])
    59             rDfs(u,++k);
    60     }
    61     for(int i=1;i <= n;++i)
    62         printf("%c
    ",tot[color[i]] > 1 ? 'T':'F');//如果当前节点所属的强连通分量编号大于1,输出'T'
    63     printf("
    ");
    64 }
    65 int main()
    66 {
    67     scanf("%d%d",&n,&m);
    68     for(int i=1;i <= m;++i)
    69     {
    70         int u,v;
    71         scanf("%d%d",&u,&v);
    72         if(find(G[u].begin(),G[u].end(),v) == G[u].end())
    73             addEdge(u,v);
    74     }
    75     Solve();
    76 }
    View Code

    H 传送门

    题干:

    题目描述
        小Q空的时候挺喜欢玩玩电脑游戏的。自从编程技术提高后,他就想,要是自己也能开发出一款游戏来,那该多好啊!不过,小Q也不着急,先练好基本功再说。Windows中就有一款叫扫雷的小游戏,挺好玩的,不过想编出一个来,还真不容易。小Q就自己设想了一种简单的扫雷游戏:在n行2列的方格棋盘上,左列某些方格内埋有地雷,而右列每个方格中都有一个数字(),第I格的数字表示:左列第I-、I、I+1格(即:上、中、下三格)中埋雷的总数。
        你的任务是:根据右列的数字分析出左列格子中的地雷(0表示无雷,1表示有雷),并且统计出左列格子中地雷的总数。
    小Q想,如果这样的任务能完成了,相信编出更复杂的扫雷游戏也就为期不远了。
    
    输入
        第一行,一个整数N(≤N≤40),第二行有N个数字(以一个空格相隔),表示右列格子中的数字。输入数据保证正确有解。
    
    
    输出
        第一行是N个0、1数字(没有空格相隔),表示左列每格中有无地雷。第二行一个整数,表示地雷总数。
    
    
    样例输入
    7
    1 2 3 2 2 2 2
    
    样例输出
    0111011
    5
    View Code

    题解:

      考察知识点:位运算??

      每个位置 i 的地雷数受 i-1,i,i+1 三个位置影响。

      1位置只受 1,2 位置影响,而 1,2 位置可能的状态有 00,01,10,11 四种(二进制数表示,0表示不含地雷,1表示含有地雷),而答案就是其中之一。

      根据 1 位置含有的地雷数枚举满足条件的 1,2 位置的状态,而 1,2 位置的状态一旦确定,通过 2 位置含有的地雷数确定 3 位置是否含有地雷,依次类推,通过 i 位置的

      地雷数确定 i+1 位置是否含有地雷,如果中间出现矛盾,枚举下一个满足条件的 1,2 位置的状态。

    AC代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 const int maxn=50;
     5 
     6 int n;
     7 int a[maxn];
     8 int status[maxn];
     9 
    10 //preStatus : (pos-2,pos-1,pos) 位置的状态对应的十进制数
    11 bool Find(int preStatus,int pos)
    12 {
    13     //preStatus中的(pos-1,pos)才对当前位置的状态有影响
    14     int curStatus=(preStatus&11);//二进制数运算,通过 &11 取出preStatus的(pos-1,pos)位置的状态
    15     int tot=(curStatus&1)+(curStatus>>1&1);//(pos-1,pos)位置含有的地雷数
    16     if(pos == n)//递归终止条件,判断是否满足最后一个位置的地雷数
    17         return tot == a[n] ? true:false;
    18     if(tot == a[pos])//如果(pos-1,pos)含有的地雷数 == a[pos],那么 pos+1 位置就不能含有地雷
    19     {
    20         status[pos]=(preStatus<<1);
    21         return Find(status[pos],pos+1);
    22     }
    23     else if(tot == a[pos]-1)//如果(pos-1,pos)含有的地雷数 == a[pos]-1,那么 pos+1 位置必须含有地雷
    24     {
    25         status[pos]=(preStatus<<1|1);
    26         return Find(status[pos],pos+1);
    27     }
    28     return false;
    29 }
    30 void Solve()
    31 {
    32     //1,2 位置可能的状态为四个二进制数 00,01,10,11,转换为十进制数为 0,1,2,3
    33     for(int i=0;i <= 3;++i)//枚举 1,2 位置的状态
    34     {
    35         int tot=(i&1)+(i>>1&1);//1,2 状态含有的地雷数
    36         if(tot == a[1])
    37         {
    38             status[1]=i;
    39             if(Find(i,2))
    40                 break;
    41         }
    42     }
    43     int res=0;
    44     for(int i=1;i < n;++i)
    45     {
    46         if(i == 1)
    47         {
    48             res += (status[1]&1)+(status[1]>>1&1);
    49             printf("%d%d",status[1]>>1&1,status[1]&1);
    50             continue;
    51         }
    52         res += (status[i]&1);
    53         printf("%d",status[i]&1);
    54     }
    55     printf("
    %d
    ",res);
    56 }
    57 int main()
    58 {
    59     scanf("%d",&n);
    60     for(int i=1;i <= n;++i)
    61         scanf("%d",a+i);
    62     Solve();
    63 }
    View Code
  • 相关阅读:
    再来五道剑指offer题目
    高强度学习训练第十天总结:Class文件
    windows linux 子系统及windows terminal的使用。
    从植物大战僵尸开始一步一步带你入门逆向工程,
    高强度学习训练第九天总结:5道剑指offer的题目
    高强度学习训练第八天总结:MySQL的一些优化
    JVM的一些工具的简要使用
    手把手教你使用Java实现一个神经网络
    指定路径创建文件,并写入数据
    c#创建windows服务(代码方式安装、启动、停止、卸载服务)
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10056692.html
Copyright © 2020-2023  润新知