为了迎合今天的讲课内容——数论,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