2017.9.22 模拟考试 解题报告

来源:互联网 发布:苹果cms多功能手机模板 编辑:程序博客网 时间:2024/05/16 02:01

2017.9.22 模拟考试 解题报告

T1

T1写写看看,一会就发现规律了,然后,除法分块,徐队说:“开莫比乌斯函数,sigma什么的一定会考除法分块”,这道题在洛谷上就叫 约数和

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long longinline LL calc(int n){    LL l=1,r,a,ret=0;    for(;l<=n;l=r+1){        a=n/l;//l,r 是区间的左右端点  a代表连续的等差的序列的被用次数        r=n/a;        ret+=(r-l+1)*(r+l)*0.5*a;    }    return ret;}int main(){    freopen("A.in","r",stdin);    freopen("A.out","w",stdout);    int x,y;    cin>>x>>y;    cout<<calc(y)-calc(x-1);    fclose(stdin);fclose(stdout);    return 0;}

等差序列求和公式
Sn=(a1+an)*n*0.5;
再详细说明一下a含义

举个栗子:
X=1,Y=12,Ans=sigma f(i)
1 2 3 4 5 6 7 8 9 10 11 12
12 6 4 3 2 2 1 1 1 1 1 1
第一个等差数列是l=1,r=1,a=12,意思就是说1作为因数贡献了12次

5、6下面对应都是2
5、10、6、12
该等差数列用了两次,恩,应该说的很明白了,当i越大的时候等差数列的长度越长,效率越高。

T2

UVa11440 原题
思路:ϕ(m!)表示小于m!并与m!互质的个数,而与m!互质的个数,他的质因子肯定不包含1-m,因此就是满足条件的。然后对于这题而言,则是要求n!中,不与m!互质的个数,答案取模100000007

求kn中与n互质的个数,答案为kϕ(n)。
ϕ(n)表示1-n中与n互质的个数,那么由此考虑[n + 1, 2n], [2n + 1, 3n]…这每个区间中的每个数字都等于1-n中数字加上kn,对于原来就与n不互质的个数,加上n仍会有一个质因子重复,所以仍然不行,那么对于原来互质的数x,gcd(x, n) = 1,那么可知gcd(x + kn, n) = 1,仍然是互质的,所以每隔n的区间与n互质的个数是相同的,所以答案kϕ(n)
所以对于这道题目,答案就变成了n!/m!ϕ(m!),那么问题只剩下如何求ϕ(m!)。
m!=1*2*3*….*m 所以对m!的阶乘质因数分解得到的因子只能是1-m中的质数。
已知ϕ(n)求法为n∗(1−1/p1)∗(1−1/p2)….(1−1/pn) (p为n的质因子),因此对于m!而言,分子为m!,分母为1 - m所有质数的(1−1/p)之乘积
到这里答案就可以求了,把m!消掉,得到n!/∏(1−1/pi)mod1000000007,先预处理那些表,每次去计算即可

#include<cstdio>using namespace std;#define MAXN 10000001#define Mod 100000007#define LL long longint prime[664580],cnt;bool v[MAXN];LL phifac[MAXN];int main(){    freopen("B.in","r",stdin);    freopen("B.out","w",stdout);    int n,m;    scanf("%d%d",&n,&m);    for(int i=2;i<MAXN;++i){        if(!v[i]) prime[++cnt]=i;        for(int j=1;j<=cnt;++j){            if(i*prime[j]>=MAXN) break;            v[i*prime[j]]=true;            if(i%prime[j]==0) break;        }    }    LL ans=0;    phifac[1]=1;    for(int i=2;i<=m;++i)        if(!v[i]) phifac[i]=phifac[i-1]*(i-1)%Mod;        else phifac[i]=phifac[i-1]*i%Mod;    ans=phifac[m];//现在ans就是phi(m!)    for(register int i=m+1;i<=n;++i) ans=ans*i%Mod; //Ans乘上n!/m!倍    printf("%I64d\n",ans-1);    return 0;}

上篇代码注释:phifac[m]表示m!的欧拉函数值
解释一下我的递推式:
 最原始的方程phi((m-1)!)=(m-1)!((P1-1)/P1) ….((Pk-1)/Pk)
 当m是质数时,phi(m!)= m*(m-1)! ((P1-1)/P1) ….((Pk-1)/Pk)((m-1)/m),中间那一块就是phi((m-1)!),==> phi(m!)= m*phi((m-1)!)((m-1)/m),两个蓝色的m再约个分,就得了上面代码里的样子了
 当m不是质数的时候,phi(m!)= m*(m-1)! ((P1-1)/P1) ….((Pk-1)/Pk),【因为m不是质数嘛,m肯定可以用p1、p2、、、、pk中的几个质因子表示】,然后中间一坨还是phi((m-1)!),于是乎又有了上面代码中的样子。。

T3

UVa6396 原题
网上有好几份题解是反素数,看了看没看懂,还是看徐队的搜索吧。

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long long int prime[25]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};LL ans,n,C[70][70];void solve(int num,int lim,LL tot,LL now,int last){    if(now>ans) return ;    if(tot==n) { ans=now; return ; }    if(tot>n || num>20) return ;    LL t=1;    for(int i=1;i<=lim;++i){        t*=prime[num];        if(now>=ans/t) return;        solve(num+1,i,tot*C[last+i][i],now*t,last+i);    }}int main(){    freopen("C.in","r",stdin);    freopen("C.out","w",stdout);    C[0][0]=1;    for(int i=1;i<70;++i){        C[i][0]=1;        for(int j=1;j<=i;++j)            C[i][j]=C[i-1][j]+C[i-1][j-1];    }    scanf("%I64d",&n);    if(n==1) { printf("1 2\n"); return 0; }    ans=(LL)1<<60;    solve(1,63,1,1,0);    printf("%I64d\n",ans);    return 0;}

这道题当时在考场上退出了反着做的正解,哈哈哈,没什么软用。
从最小的质数开始枚举选几个
假设前i-1个种质数用了k个,有Np种方案,第i种质数选a个,
那么前i种质数的方案就有Np*C[k+a][a]
可以理解原来有k个位置,又加了a个位置,有a个数可以放在任意位置
所以前i种的每一种方案都变成C[k+a][a]种
枚举每个质数选几个时,如果上一个质数选了k个,那么这一个质数最多选k个
假设这个质数选了k+1个,那么显然上一个质数选k+1个,这个选k个更优,就是说大质数陪小底数,小大,大小。

原创粉丝点击