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\n",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\n");
    else printf("%d\n",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:

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

posted @ 2018-08-10 22:18  radishえらい  阅读(293)  评论(0编辑  收藏  举报