• 2018.8.10提高B组模拟考试


    为了迎合今天的讲课内容——数论,A组和B组都各出了两道数学。

    对于完全不会数论的博主来说,这简直是灾难。

    T1 题意简述:jzoj5791

    Description

    有n个正整数a[i],设它们乘积为p,你可以给p乘上一个正整数q,使p*q刚好为正整数m的阶乘,求m的最小值。

    Input

    共两行。
    第一行一个正整数n。
    第二行n个正整数a[i]。

    Output

    共一行
    一个正整数m。

    Data Constraint

    对于10%的数据,n<=10
    对于30%的数据,n<=1000
    对于100%的数据,n<=100000,a[i]<=100000

       解题思路:看到这道题,首先想到对n个数分别分解成质数后存在数组里。

                 然后呢?枚举ans吗?

                 其实可以二分答案,加上一个求质数个数的技巧就能过。

                 发现cnt[2]=ans/2+ans/4+ans/8+...

                     cnt[3]=ans/3+ans/9+ans/27+...

                 本题结束。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    #define ll long long
    using namespace std;
    ll n,tot,ans=INF,pri[100001],prime[100001],cnt[100001];
    void getpri()
    {
        pri[1]=1;
        for(ll i=2;i<=100000;i++)
            if(!pri[i])
            {
                ll j=2;
                while(i*j<=100000)
                    pri[i*j]=1,j++;
                prime[++prime[0]]=i;
            }
    }
    int main()
    {
        freopen("factorial.in","r",stdin);
        freopen("factorial.out","w",stdout);
        scanf("%lld",&n);
        getpri();
        for(ll i=1;i<=n;i++)
        {
            ll u;
            scanf("%lld",&u);
            for(ll i=1;i<=prime[0];i++)
            {
                if(u==1) break;
                if(!pri[u]) {cnt[u]++,tot++;break;}
                if(!(u%prime[i])) while(!(u%prime[i])) u/=prime[i],cnt[prime[i]]++,tot++;
            }
        }
        ll l=1,r=5000000;
        while(l<r)
        {
            ll mid=(l+r)>>1,flag=1;
            for(ll i=1;i<=prime[0];i++)
            {
                ll tmp=1,num=0;
                while(tmp*prime[i]<=mid)
                {
                    tmp*=prime[i];
                    num+=mid/tmp;
                }
                if(num<cnt[prime[i]]) {flag=0;break;}
            }
            if(flag) r=mid,ans=min(ans,mid);
            else l=mid+1;
        }
        printf("%lld
    ",ans);
        return 0;
    }

    T2 题意简述:jzoj5793

    Description

          小S是一个爱锻炼的孩子,他在放假期间坚持在A公园练习跑步。
          但不久后,他就开始为在重复的地点练习感到厌烦了,他就打算去B公园跑步。
          但是小S由于没有去过B公园,他不知道B公园是否适合练习跑步,又不知道在B公园怎样跑是最优的。所以小S就去B公园进行了一番勘测。
          小S在进行了一番勘测后,画出了一张地图,地图每一个位置上都辨识了小S到达该位置后不能往哪一个方位移动。其中有5种表示的符号:“U”代表不能向上移动,“D”代表不能向下移动,“L”代表不能向左移动,“R”代表不能向右移动,如果该位置有障碍物,小S到达那里后无法继续训练,就用“S”来代表。整个公园共有n行m列,小S会从第1行第1列出发,到第n行第m列结束他的练习。
          现在小S想要知道,从起点(即第1行第1列)到终点(第n行第m列),途中最少要改变几次方向(即上一次移动的方向和这一次移动的方向不一样)?
          注意:小S如在训练途中离开公园(即地图范围),则算是结束训练。

    Input

          第1行两个整数n和m,它们的定义请看【题目描述】。
          第2~n+1行,每行有m个字符,表示小S的移动方向。

    Output

          如果小S从第1行第1列出发无论如何都到达不了第n行第m列,输出“No Solution”,否则输出小S途中最少要改变方向的次数。

    Data Constraint

    【数据范围限制】
    10%的数据是题目的馈赠。
    30%的数据,1≤n,m≤10。
    50%的数据,1≤n,m≤50。
    70%的数据,1≤n,m≤250。
    100%的数据,1≤n,m≤500.
    其中50%的数据是先构造出路径,再构造地图的。
    100%数据是随机构造的。

       解题思路:首先本蒟蒻强烈谴责出题人!

                 好吧其实出题人说的没错。

                 这题的官方题解是BFS,其实dijkstra也可以水过。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m,ans=INF,a[501][501],step[250001][5],vis[250001][5];
    char s[501];
    queue<int> que,que1;
    void bfs()
    {
        que.push(1);que.push(1);
        que1.push(2);que1.push(4);
        vis[1][2]=vis[1][4]=1;
        step[1][2]=step[1][4]=0;
        while(!que.empty())
        {
            int now=que.front(),dir=que1.front();
            que.pop();que1.pop();
            vis[now][dir]=0;
            int x=now/m+1,y=now%m;
            if(!y) x--,y+=m;
            if(!a[x][y]) continue;
            if(a[x][y]!=1&&x!=1)
            {
                int old=step[now-m][1];
                if(dir!=1) step[now-m][1]=min(step[now-m][1],step[now][dir]+1);
                else step[now-m][1]=min(step[now-m][1],step[now][dir]);
                if(!vis[now-m][1]&&step[now-m][1]<old) vis[now-m][1]=1,que.push(now-m),que1.push(1);
            }
            if(a[x][y]!=2&&x!=n)
            {
                int old=step[now+m][2];
                if(dir!=2) step[now+m][2]=min(step[now+m][2],step[now][dir]+1);
                else step[now+m][2]=min(step[now+m][2],step[now][dir]);
                if(!vis[now+m][2]&&step[now+m][2]<old) vis[now+m][2]=1,que.push(now+m),que1.push(2);
            }
            if(a[x][y]!=3&&y!=1)
            {
                int old=step[now-1][3];
                if(dir!=3) step[now-1][3]=min(step[now-1][3],step[now][dir]+1);
                else step[now-1][3]=min(step[now-1][3],step[now][dir]);
                if(!vis[now-1][3]&&step[now-1][3]<old) vis[now-1][3]=1,que.push(now-1),que1.push(3);
            }
            if(a[x][y]!=4&&y!=m)
            {
                int old=step[now+1][4];
                if(dir!=4) step[now+1][4]=min(step[now+1][4],step[now][dir]+1);
                else step[now+1][4]=min(step[now+1][4],step[now][dir]);
                if(!vis[now+1][4]&&step[now+1][4]<old) vis[now+1][4]=1,que.push(now+1),que1.push(4);
            }
        }
    }
    int main()
    {
        freopen("run.in","r",stdin);
        freopen("run.out","w",stdout);
        memset(step,0x3f,sizeof(step));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
            {
                if(s[j]=='U') a[i][j]=1;
                if(s[j]=='D') a[i][j]=2;
                if(s[j]=='L') a[i][j]=3;
                if(s[j]=='R') a[i][j]=4;
            }
        }
        bfs();
        for(int i=1;i<=4;i++)
            ans=min(ans,step[n*m][i]);
        if(ans==INF) printf("No Solution
    ");
        else printf("%d
    ",ans);
        return 0;
    }

    T3 题意简述:jzoj5787

    Description

    2018年1月31日,152年一遇的超级大月全食在中国高空出现(没看到的朋友真是可惜),小B看到月食,便对月球的轨道产生了兴趣。他上网查重力加速度的公式,公式如下:


    就在这个时候,他想到了一个跟这个差不多的问题,那就是对于以下公式:

    已知n和k,求这n个正整数在都不大于m的情况下有多少种选择方式,使得v为与k互质正整数?

    Input

    一行三个正整数n,m,k(意义见题目描述)。

    Output

    输出一个答案,代表方案数。因为答案可能会很大,所以输出方案数mod 10007的值。

    Data Constraint

    数据范围
    对于20%的数据 1<=n,m<=8 k<=100
    对于40%的数据 1<=n<=50 1<=m<=10^6 1<=k<=10^4
    对于70%的数据 1<=n<=100 1<=m<=10^9 1<=k<=10^7
    对于100%的数据 1<=n<=3000 1<=m<=10^9 1<=k<=10^7

       解题思路:首先本蒟蒻再次强烈谴责出题人!

                 然后他这几个问题ppt里都没给答案。

                 我:???

                 最重点的是在这五连询问后面跟了一句这个:

                  好吧其实这题是这么做的。

                 首先想到DP。(怎么想到的?我也不知道)

                 设dp[i][j]记录枚举到第i个数,这i个数的乘积为j。

                 发现数组根本开不下,考虑精简。

                 发现对于乘积我们需要知道的只有它和k的最大公约数。

                 因此修改dp[i][j]为枚举到第i个数,这i个数的乘积与k的最大公约数是k的第j个约数。

                 现在咱们来解决他提出的第一个问题:为什么与k的最大公约数一定是k的约数呢?

                 解答:这还用解决?这不是性质吗2333

                 考虑转移。发现dp[i][j]=dp[i-1][k]*dp[1][a[j]/a[k]是k的第几个约数]。

                 这里a[i]表示k的第i个小于等于m的约数,以后的a[i]也相同。

                 现在咱们来解决他提出的第二个问题:为什么a[j]/a[k]一定是k的约数呢?

                 解答:参见第一问解答。(2333)

                 考虑求出dp[1][1~k的约数个数]。发现答案即为在1~m中与k的最大公约数是a[i]的个数。

                 设这个问题为问题x,那么可以先枚举1~m,再枚举a[i]即可。

                 现在咱们来解决他提出的第三个问题:问题x的原理是什么呢?

                 解答:...如果您不知道,请阅读上文中dp[i][j]的定义。

                 发现这个求dp[1][i]的方法的速度太慢,只能过40%的点。考虑优化。

                 然后这里就是精髓了。出题人想到了容斥,但没有给出问题四的答案。

                 又因为本博主太蒟,没能想出容斥系数。

                 所以这篇博客暂时就到这里了。关于之后的部分本蒟蒻会和各位大佬研究后再补上。

                 8.12 fix:

                 具体思路及代码放在了这里

  • 相关阅读:
    自适应行高
    IOS各类问题
    KVC
    数据模型的构建及懒加载数据
    NSBundle
    九宫格布局获取行/列索引
    QLineEdit
    QLabel
    排序算法
    SpringBoot配置文件-yaml
  • 原文地址:https://www.cnblogs.com/water-radish/p/9457790.html
Copyright © 2020-2023  润新知