zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    codevs 2632 非常好友
    codevs 1213 解的个数
    codevs 2751 军训分批
    codevs 1519 过路费
    codevs 1503 愚蠢的宠物
    codevs 2639 约会计划
    codevs 3369 膜拜
    codevs 3135 River Hopscotch
    数论模板
    JXOJ 9.7 NOIP 放松模拟赛 总结
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10056692.html
Copyright © 2011-2022 走看看