• UPC 2019年第二阶段我要变强个人训练赛第十六场


    传送门:

      [1]:UPC比赛场

      [2]:UPC补题场

    F.gu集合(数论)

    •题目描述

     1 题目描述:
     2 Dew有一个长为n的集合S。
     3 有一天,他想选k个不同的元素出来做游戏。
     4 但是Dew只有两只手,所以他只能先选出k个元素,然后拿出这k个元素中最小的两个。
     5 事实上,Dew更喜欢这k个元素中第二小的那个。
     6 因此他会记一个集合T的第二小值为g(T)。
     7 此时Dew可以获得c^g(T)!的得分,其中c是一个常数,!表示阶乘。
     8 现在你需要求出Dew从集合S中选出k个元素后,他的期望得分对998244353取模的结果。
     9 
    10 输入:
    11 输入共两行。
    12 第一行三个正整数n,k,c,分别表示集合S的大小,Dew要选的元素个数,和常数c。
    13 第二行n个互不相同的正整数ai,表示集合S中的元素。保证集合s升序。
    14 
    15 输出:
    16 输出一行一个非负整数,表示Dew的期望得分对998244353取模的结果。
    题目描述、输入、输出
    1 样例输入
    2 5 3 2
    3 1 2 3 4 5
    4 
    5 样例输出
    6 803628674
    样例输入输出

    样例解释

    •题解

      在tyk的帮助下顺利AC;

      下面开始扯淡;

      第 i 个数 ai 可以作为第二小的数的条件是:

        ①存在比 ai 小的数,即 i > 1;

        ②比 ai 大的数的个数要多于 k-2,即 n-i ≥ k-2;

      对于满足条件的 ai,共有

      

      那么得到 ai 的概率为:

      

      最终答案就是 ∑ Pi×cai!

    •细节处理

      对于除法取模Pi%MOD直接用逆元处理即可,关键是 cai该如何处理

      根据费马小定理:ap-1 ≡ 1 (mod p) 可知循环节为 p-1(ap-1 ≡ a0 (mod p) ⇔ 0~p-2 为一个循环) ;

      即 ax ≡ ax%(p-1) (mod p) ;

      那么 cai≡ cai!%(p-1)(mod p)

      而 ai!%(p-1) 可以提前预处理出来;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const int MOD=998244353;
     5 const int maxn=5e5+50;
     6 
     7 int n,k,c;
     8 int a[maxn];
     9 ll fact[maxn];///fact只开到5e5就足够了,如果开到1e7,返回内存超限
    10 ll g[(int)1e7+50];
    11 
    12 ll qPow(ll a,ll b)
    13 {
    14     ll ans=1;
    15     a %= MOD;
    16     while(b)
    17     {
    18         if(b&1)
    19             ans=ans*a%MOD;
    20 
    21         a=a*a%MOD;
    22         b >>= 1;
    23     }
    24     return ans;
    25 }
    26 int Solve()
    27 {
    28     fact[0]=g[0]=1;
    29     for(int i=1;i < maxn;++i)
    30         fact[i]=i*fact[i-1]%MOD;
    31     for(int i=1;i <= (int)1e7;++i)
    32         g[i]=i*g[i-1]%(MOD-1);
    33 
    34     ll ans=0;
    35     for(int i=2;i <= n;++i)
    36     {
    37         if(n-i < k-2)
    38             break;
    39 
    40         ll b=fact[k-2]*fact[n-i-k+2]%MOD*fact[n]%MOD;///概率的分母取模后的结果
    41         ///qPow(b,MOD-2)为逆元
    42         ll p=(i-1)*fact[n-i]%MOD*fact[k]%MOD*fact[n-k]%MOD*qPow(b,MOD-2)%MOD;
    43 
    44         ans += p*qPow(c,g[a[i]]);
    45         ans %= MOD;
    46     }
    47     return ans;
    48 }
    49 int main()
    50 {
    51     scanf("%d%d%d",&n,&k,&c);
    52     for(int i=1;i <= n;++i)
    53         scanf("%d",a+i);
    54 
    55     printf("%d
    ",Solve());
    56 
    57     return 0;
    58 }
    View Code

    •感悟

      满满的数论知识,有些知识点早就学过,但就学死了,没能做到活用;

      看来,还是得多做题,多总结,要活学活用;


    H.奇迹(暴力+欧拉筛法)

    •题目描述

    题目描述
    相信奇迹的人,本身就和奇迹一样了不起。——笛亚 《星游记》
    我们称一个日期为一个八位数,第1~4位构成年,第5~6位构成月,第7~8位构成日,不足位数用0补足。
    同时,要求日期所代表的这一天真实存在,且年的范围为1~9999。
    出现奇迹的日期都存在相同的特点:由“日”组成的两位数,由“月+日”组成的四位数,由“年+月+日”组成的八位数均为质数。
    但并不是所有存在这样特点的日期都一定会出现奇迹。
    现在,你得到了一个可能会出现奇迹的日期,然而不幸的是这个日期却是残缺的,八位中可能有若干位无法确定。
    你需要知道这个日期有多少种可能,这样你才能做好充足的准备去迎接奇迹的到来。
    
    输入
    本题有多组数据。
    第一行一个正整数T,表示数据组数。
    接下来的T行,每行一个八位字符串。
    其中第i位如果为 '-',则表示日期的第i位无法确定,否则表示日期的第i位为字符串中第i位上的数字。
    
    输出
    对每组数据,一行一个整数,表示答案。
    题目描述、输入、输出
    样例输入
    2
    53-7-3-7
    20190629
    
    样例输出
    6
    0
    样例输入输出
    53-7-3-7 的  种可能的日期如下:
    53070307
    53070317
    53170307
    53370307
    53570317
    53770307
    
    一共10个测试点,记c为八位字符串中 '-' 的个数。
    对前9个测试点,在第i个测试点中保证c=i-1。
    对100%的数据保证1≤T≤10
    提示

    •题解(暴力+欧拉筛法预处理)

      枚举所有可能的日期,并判断①其是否符合字符串 s;②符合正常日期;

      在满足上述条件的请款下,并判断其是否符合出现奇迹的要求,如果符合,ans++;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mem(a,b) memset(a,b,sizeof(a))
     4 const int day[2][20]={{0,31,28,31,30,31,30,31,31,30,31,30,31},
     5                       {0,31,29,31,30,31,30,31,31,30,31,30,31}/**闰年*/};
     6 char s[100];
     7 
     8 int prime[10000000];
     9 bool isPrime[99991232];
    10 void Prime()
    11 {
    12     mem(isPrime,true);
    13     int cnt=0;
    14     isPrime[1]=false;
    15     for(int i=2;i < 99991232;++i)
    16     {
    17         if(isPrime[i])
    18             prime[++cnt]=i;
    19         for(int j=1;j <= cnt && prime[j]*i < 99991232;++j)
    20         {
    21             isPrime[i*prime[j]]=false;
    22             if(i%prime[j] == 0)
    23                 break;
    24         }
    25     }
    26 }
    27 bool isRun(int y)
    28 {
    29     return (y%100 != 0 && y%4 == 0)||(y%400 == 0);
    30 }
    31 bool isSatY(int y)
    32 {
    33     if(s[1] != '-' && (s[1]-'0') != y/1000)
    34         return false;
    35     if(s[2] != '-' && (s[2]-'0') != y/100%10)
    36         return false;
    37     if(s[3] != '-' && (s[3]-'0') != y/10%10)
    38         return false;
    39     if(s[4] != '-' && (s[4]-'0') != y%10)
    40         return false;
    41 
    42     return true;
    43 }
    44 bool isSatM(int m)
    45 {
    46     if(s[5] != '-' && (s[5]-'0') != m/10)
    47         return false;
    48     if(s[6] != '-' && (s[6]-'0') != m%10)
    49         return false;
    50 
    51     return true;
    52 }
    53 bool isSatD(int d)
    54 {
    55     if(s[7] != '-' && (s[7]-'0') != d/10)
    56         return false;
    57     if(s[8] != '-' && (s[8]-'0') != d%10)
    58         return false;
    59 
    60     return true;
    61 }
    62 int Solve()
    63 {
    64     int ans=0;
    65     for(int y=1;y <= 9999;y++)
    66     {
    67         if(!isSatY(y))///判断y是否符合s
    68             continue;
    69 
    70         for(int m=1;m <= 12;m++)
    71         {
    72             if(!isSatM(m))///判断m是否符合s
    73                 continue;
    74 
    75             for(int d=1;d <= day[isRun(y)][m];d++)///判断m是否符合s
    76                 if(isSatD(d) && isPrime[m*100+d] && isPrime[d] && isPrime[y*10000+m*100+d])
    77                     ans++;
    78         }
    79     }
    80     return ans;
    81 }
    82 int main()
    83 {
    84     Prime();///预处理1~99991232内的所有素数
    85 
    86     int T;
    87     scanf("%d",&T);
    88     while(T--)
    89     {
    90         scanf("%s",s+1);
    91         printf("%d
    ",Solve());
    92     }
    93     return 0;
    94 }
    View Code

    思考

    上述代码耗时:

    如果将三重循环的顺序颠倒以下,改成如下顺序:

     1 int Solve()
     2 {
     3     int ans=0;
     4     for(int d=1;d <= 31;++d)
     5     {
     6         if(!isPrime[d] || !isSatD(d))
     7             continue;
     8         for(int m=1;m <= 12;++m)
     9         {
    10             if(!isPrime[d+m*100] || !isSatM(m))
    11                 continue;
    12             for(int y=1;y <= 9999;++y)
    13             {
    14                 if(!isPrime[y*10000+m*100+d] || d > day[isRun(y)][m] || !isSatY(y))
    15                     continue;
    16                 ans++;
    17             }
    18         }
    19     }
    20     return ans;
    21 }
    View Code

    耗时:

    快了将近三百毫秒!!!

    对于此题而言,颠倒一下顺序,枚举的情况会少一下;

    •坑

    起初,我用vector<>存储的prime,返回 TLE,改成 int 后,AC;

    看来,动态分配空间还是比提前计算出所需的空间慢一些;

  • 相关阅读:
    BizTalk开发系列(三十一)配置和使用HTTP适配器
    BizTalk开发系列(三) 单机环境下的BizTalk Server 2006 R2安装
    BizTalk开发系列(二) "Hello World" 程序搬运文件
    BizTalk开发系列(三十二)浅谈BizTalk主机性能优化
    BizTalk开发系列(二十四) BizTalk项目框架建议
    BizTalk开发系列(二十八) MSMQ 适配器
    BizTalk开发系列(三十五) TCP/IP 适配器
    BizTalk开发系列(十四) XML空白字符(WhiteSpace)
    BizTalk开发系列(一) "Hello World"
    使用Xpath对XML进行模糊查询
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11199935.html
Copyright © 2020-2023  润新知