您的位置:首页 > 其它

数学专题训练2

2014-02-26 22:02 218 查看
这次是数论专题训练。。。这几道题总的来说:没有最坑,只有更坑!!

题目一:Uva11426

题目大意:输入正整数 n 求Σgcd(i,j) 1<=i<j<=n

设f
=gcd(1,n)+gcd(2,n)+gcd(3,n)...+gcd(n-1,n);

那么答案自然就是 Σf[i] 2<=i<=n

关键问题在这个f
怎么求上。。。

假设gcd(x,n)=i;那么 gcd(x/i,n/i)=1;

符合这个条件的x的个数实际上就是小于x/i且与x/i互质的数的个数-->欧拉函数

于是f
=Σ phi(n/i)( i为n的因子切i != n)

如果对于每个f[i]枚举它的因子。。。复杂度高了

于是就这样处理:枚举因子i,再枚举它的倍数j(i<j<=n)。。。f[j]+=phi[j/i];

...这道题就解决了

然后要注意的一个地方是有多组输入。。。但是每次输出的答案的值都可以一次性求出来。。于是就先预处理出来 :s
表示输入为n时的答案。。。。明显s
=s[n-1]+f
;

至于如何求出phi。。。也是先用一个类似筛法的方法预处理一次。。注意phi[1]=1; (我开始只预处理了phi,T了半天。。。)

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define maxn 4000000
int n;
long long f[maxn+10],s[maxn+10];
int phi[maxn+10];
void make_phi(int u)
{
phi[1]=1;
for(int i=2;i<=u;i++)
{
if(!phi[i])
for(int j=i;j<=u;j+=i)
{
if(!phi[j])phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
int main()
{
make_phi(maxn);
for(int i=1;i<=maxn;i++)
{
for(int j=i*2;j<=maxn;j+=i)
{
f[j]+=(long long)i*phi[j/i];
}
}
s[2]=f[2];
for(int i=3;i<=maxn;i++)s[i]+=s[i-1]+f[i];
while(scanf("%d",&n)!=EOF)
{
if(n==0)break;
cout<<s
<<endl;
}
return 0;
}


题目二: LA4119 Always an integer

题目大意:输入一个次数严格递减的多项式,判断它是否总是整数。。多项式输入格式(只有整数多项式)/某个数

。。。根据lrj《算法入门经典训练指南》的分析, 只需要把1到最高次+1 带进去判断就行了。。。如果出现了分子多项式值 % 分母!=0 就不是。。否则就是。。

具体分析还是有很长。。就不写了

其实这道题难点还在字符串处理上。。。我一直wa,最后靠对拍才完全弄对。。

#include<cstdlib>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long mod;
int k;
char temp[1000+10];
long long A[100+10];
long long qpow(long long d,int c)
{
if(c==0)return 1;
if(c==1)return d;
long long ret=qpow(d,c/2);
ret=(ret*ret)%mod;
if(c&1)ret=(ret*d)%mod;
return ret;
}
bool check(int x)
{
long long ans=0;
for(int i=k;i>=0;i--)
{
ans=(ans+(A[i]%mod*qpow(x,i)%mod)%mod)%mod;
}
if(ans%mod==0)return true;
else return false;
}
int getk()//顺便把mod解决了吧
{
int len=strlen(temp);
int ret=0;
int i;
for(i=0;i<len;i++)
{
if(temp[i]=='n')
{
i++;
if(temp[i]=='^')
{
i++;
int tem=temp[i]-'0';
while(temp[i+1]<='9'&&temp[i+1]>='0')
{
i++;
tem*=10;
tem+=temp[i]-'0';
}
A[tem]=1;
if(tem>ret)ret=tem;
}
else A[1]=1;if(ret<1)ret=1;
}
else if(temp[i]=='-'||temp[i]<='9'&&temp[i]>='0')
{
long long tem1=0;
bool f=0;
if(temp[i]=='-')f=1;
else tem1=temp[i]-'0';
if(f&&!(temp[i+1]<='9'&&temp[i+1]>='0'))tem1=1;
while(temp[i+1]<='9'&&temp[i+1]>='0')
{
i++;
tem1*=10;
tem1+=temp[i]-'0';
}
if(f)tem1=-tem1;
if(temp[i+1]=='n')
{
i++;
if(temp[i+1]=='^')
{
i+=2;
int tem=temp[i]-'0';
while(temp[i+1]<='9'&&temp[i+1]>='0')
{
i++;
tem*=10;
tem+=temp[i]-'0';
}
ret=max(ret,tem);
A[tem]=tem1;
}
else
{
A[1]=tem1;
if(ret<1)ret=1;
}
}
else
{
A[0]=tem1;
}
}
if(temp[i]=='/')break;
}
long long tem=0;
for(i=i+1;i<len;i++)
{
tem*=10;
tem+=temp[i]-'0';
}
mod=tem;
return ret;
}
int main()
{
int cas=0;
while(scanf("%s",temp)!=EOF)
{
cas++;
if(temp[0]=='.')break;
memset(A,0,sizeof(A));
k=getk();
bool ok=true;
for(int i=0;i<=k+1;i++)
{
if(!check(i))
{
ok=false;
break;
}
}
if(ok)printf("Case %d: Always an integer\n",cas);
else printf("Case %d: Not always an integer\n",cas);
}
return 0;
}


题目三:wikioi1213 解的个数

这个嘛。。。其实差不多是扩展欧几里得的裸题。。。不过数据有些坑点。。。特殊情况比较多,还会爆int。。。所以也卡了半天。。。最后看数据才改对的。。。

注意的是:解的个数并不需要枚举。。而是直接一步求出边界(可能需要调整)。。。

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int n;
long long a,b,c,p,q,r,s;
bool special()
{
if(a==0)
{
if(b==0)
{
if(c==0)
printf("%lld\n",(q-p+1)*(s-r+1));
else printf("0\n");
return true;
}
else
{
if(c%b==0&&r<=(-c)/b&&(-c)/b<=s)printf("%lld\n",q-p+1);
else printf("0\n");
return true;
}
}
else if(b==0)
{
if(c%a==0&&p<=(-c)/a&&(-c)/a<=q)printf("%lld\n",s-r+1);
else printf("0\n");
return true;
}
return false;
}
long long gcd(int a,int b)
{
if(b==0)return a;
else return gcd(b,a%b);
}
void exgcd(long long &x,long long &y,long long a,long long b)
{
if(b==0)
{
x=1;y=0;
}
else
{
exgcd(y,x,b,a%b);
y-=x*(a/b);
}
}
int main()
{
cin>>n;
while(n--)
{
cin>>a>>b>>c>>p>>q>>r>>s;
if(p>q||r>s){printf("0\n");continue;}
if(special())continue;//特判
long long d=gcd(a,b);
if(c%d!=0)printf("0\n");
else
{
long long a1=a/d,b1=b/d,c1=c/d;
long long x,y;
exgcd(x,y,a1,b1);
x=x*(-c1);
y=y*(-c1);
//计算x的边界,用其限制y的边界
long long xmax=(q-x)/b1*b1+x,xmin=x-(x-p)/b1*b1;
if(xmax>q)xmax-=b1;
if(xmin<p)xmin+=b1;
if(xmax<xmin){printf("0\n");continue;}
long long ymax=(-c1-a1*xmax)/b1,ymin=(-c1-a1*xmin)/b1;
if(ymax<ymin)swap(ymax,ymin);
//计算y的边界
long long ymax1=(s-y)/a1*a1+y,ymin1=y-(y-r)/a1*a1;
if(ymin1<r)ymin1+=a1;
if(ymax1>s)ymax1-=a1;
if(ymax1<ymin1){printf("0\n");break;}
//合并范围
ymax=min(ymax,ymax1);
ymin=max(ymin,ymin1);
printf("%lld\n",(ymax-ymin)/abs(a1)+1);
}
}
return 0;
}


题目四:poj 2891

中国剩余定理的裸题。。。不过这个的a不都互素。。。所以要一次合并两个方程。。

对于:

x=r1(mod a1)

x=r2(mod a2)

可以写成 x=r1+a1*k1=r2*a2*k2

然后用扩展欧几里得解 a1*k1-a2*k2=r2-r1的k的最小正值

然后带回x=r1+a1*k1得到x的解。。。

于是方程就被合并成了 x=r1+a1*k1(mod (a1,a2))。。。

当有多个方程组的时候把先两个合并成一个,再把它拿来继续和下一个方程合并。。。

合并到最后答案也就很明显了- -

注意写的代码里面a,r是反的。。。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n;
LL aaa,rrr;
LL a1,b1,c1;
LL n1,m;
LL nowa,nowr;
LL gcd(LL a,LL b)
{
if(!b)return a;
else return gcd(b,a%b);
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1;y=0;return a;
}
else
{
LL ret=exgcd(b,a%b,y,x);
y-=x*(a/b);
return ret;
}
}
int main()
{
while(cin>>n)
{
cin>>nowr>>nowa;
bool flag=false;
for(LL i=2;i<=n;i++)
{
cin>>rrr>>aaa;
if(flag)continue;
a1=nowr,b1=rrr;c1=aaa-nowa;
LL d=exgcd(a1,b1,n1,m);
if(d==0||c1%d!=0)
{
flag=1;
continue;
}
LL temp=b1/d;
LL x=((n1*c1/d)%temp+temp)%temp;
nowa=x*nowr+nowa;
nowr=nowr/d*rrr;
}
if(!flag)cout<<nowa<<endl;
else cout<<-1<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: