2018.8.10提高B组模拟考试

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

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

T1 题意简述:jzoj5791

解题思路:看到这道题,首先想到对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

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

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

这题的官方题解是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

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

然后他这几个问题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%的点。考虑优化。

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

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

             所以这篇博客暂时就到这里了。关于之后的部分本蒟会和身边的各位大佬研究后再放出来。

原文地址:https://www.cnblogs.com/water-radish/p/9457790.html

时间: 2024-10-31 14:06:21

2018.8.10提高B组模拟考试的相关文章

2018.8.6提高A组模拟考试

emmm看起来A组的题也没比B组难多少嘛... T1 题意简述:jzoj5796 解题思路:首先发现数字是输入数据中最特殊的字符,因此可以在找到数字后向四周dfs找到水箱边界. 其次发现题目中说“不会把水管分叉也不会出现水管交叉的情况”,因此可以在找到水箱后用dfs求 出每根水管的位置及其连接的水箱. 然后根据基本的物理原理(牛顿笑着躺下了)发现若同一水箱所连的两根水管所处位置高低不同,那 么在填满低水管所连的水箱之前,高水管所连的水箱是不会有水进入的. 综上,只需dfs一遍即可得出水箱灌满的次

2018.8.7提高B组模拟考试

T1 题意简述:jzoj5461 解题思路:贪心. 考虑先用完k张优惠券,只需将商品按优惠价排序,依次判断钱是否足够,若是则ans++,否则直接退 出循环并输出ans即可. k张优惠券用完后若钱还未用完,则考虑用前面商品的优惠券买后面的商品.注意要把未买的商品按 原价重新排序.只需把用了优惠券的商品的原价与优惠价之差价放进一个小根堆,每次判断:此商品 原价是否小于堆顶商品差价+此商品优惠价.若是则直接用原价买即可,否则用堆顶商品差价+此商品 优惠价买即可.注意有0张优惠券的情况,因此要特判队列是

2018.8.9提高B组模拟考试

今天连续3道题都出锅...无F♂A可说 T1 题意简述:jzoj5775 解题思路:还算简单... 考虑先暴力算出(1,1)的魔音值,然后递推. 发现 (x,y)->(x+1,y) 魔音值增加了1~x行中起始点的数量,减少了x+1~n行中起始点的数量. (x,y)->(x-1,y) 魔音值增加了x~n行中起始点的数量,减少了1~x-1行中起始点的数量. (x,y)->(x,y+1) 魔音值增加了1~y列中起始点的数量,减少了y+1~n列中起始点的数量. (x,y)->(x,y-1)

【NOIP2016提高A组模拟10.15】最大化

题目 分析 枚举两个纵坐标i.j,接着表示枚举区域的上下边界, 设对于每个横坐标区域的前缀和和为\(s_l\),枚举k, 显然当\(s_k>s_l\)时,以(i,k)为左上角,(j,k)为右下角的矩阵一定合法. k从小到大,维护一个单调队列, 显然当\(l1<l2\)时 如果\(s_{l1}<s_{l2}\),l2一定对答案没有贡献,就不将其加入单调队列. 对于一个k,在单调队列中二分,枚举出一个最小的位置,并且\(s_k>s_l\). #include <iostream&

【NOIP2016提高A组模拟10.15】算循环

题目 分析 一步步删掉循环, 首先,原式是\[\sum_{i=1}^n\sum_{j=1}^m\sum_{k=i}^n\sum_{l=j}^m\sum_{p=i}^k\sum_{q=j}^l1\] 删掉最后两个循环 \[\sum_{i=1}^n\sum_{j=1}^m\sum_{k=i}^n\sum_{l=j}^m(k-i+1)(l-j+1)\] 发现,当\(i,j\)固定,随着\(k,l\)的变化,\((k-i+1),(l-j+1)\)都是每次减少1 SO, \[\sum_{i=1}^n\su

NOIP2016提高A组模拟10.15总结

第一题,就是将原有的式子一步步简化,不过有点麻烦,搞了很久. 第二题,枚举上下边界,维护一个单调队列,二分. 比赛上没有想到,只打了个暴力,坑了80分. 第三题,贪心,最后的十多分钟才想到,没有打出来. 心得 1.首先感谢出题人,暴力分好多. 2.但是,比赛期间,我在交头接耳,浪费了很多时间.导致时间不够. 原文地址:https://www.cnblogs.com/chen1352/p/9066567.html

【NOIP2016提高A组模拟8.15】Garden

题目 分析 其实原题就是[cqoi2012][bzoj2669]局部极小值. 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值. 给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵. 发现,X的位置最多有8个,那我们考虑状压dp. 我们从小到大把数填进去,用\(f_{i,j}\)表示,把第i个数填进去后,每个X是否被填了数,用二进制数j表示. 预处理出\(rest_j\)表示填充状态

【NOIP2016提高A组模拟9.15】Osu

题目 分析 考虑二分答案, 二分小数显然是不可取的,那么我们将所有可能的答案求出来,记录在一个数组上,排个序(C++调用函数很容易超时,手打快排,时间复杂度约为\(O(>8*10^7)\),但相信梦想的力量). 剩下就简单了,将二分出的值判断是否可以获得k分以上, 这里可以用多种方法,spfa.dp dp: \(dp_i\)表示移动到了第i个点的最大分数 #include <cmath> #include <iostream> #include <cstdio>

【NOIP2016提高A组模拟9.17】小a的强迫症

题目 分析 题目要求第i种颜色的最后一个珠子要在第i+1种颜色的最后一个珠子之前, 那么我们从小到大枚举做到第i种,把第i种的最后一颗珠子取出,将剩下的\(num(i)-1\)个珠子插入已排好的前i-1种中,再将取出的珠子放在最后一个. 每次求出将剩下的\(num(i)-1\)个珠子插入已排好的前i-1种中的方案数,将它乘以ans. 对于每个i的方案数可以用隔板问题来求. 但是,在比赛上,我忘了隔板问题,于是再枚举个j,将已经排好的珠子分成j份,将要放进去的的\(num(i)-1\)个珠子找出j