数学专题训练2

6 篇文章 0 订阅
4 篇文章 0 订阅

这次是数论专题训练。。。这几道题总的来说:没有最坑,只有更坑!!

题目一:Uva11426

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


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

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

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

假设gcd(x,n)=i;那么 gcd(x/i,n/i)=1;
符合这个条件的x的个数实际上就是小于x/i且与x/i互质的数的个数-->欧拉函数

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

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

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

...这道题就解决了

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

至于如何求出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[n]<<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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值