1. 程式人生 > 實用技巧 >備戰noip week1

備戰noip week1

Pairs of integers

題意:給出一個整數N(N<=1e9)求兩個整數X,Y使得X+Y=N,求所有的解
其中Y是X直接去掉一位後得到的(1034->134,100->00.etc)
題解:對於整數X,我們列舉它的每一位(第i位),並且列舉這一位上所有可能的數字d
令第i位前的數字構成的數為x,第i位後的數字構成的數為y
那麼就有\(x*10^i+d*10^{i-1}+y+x*10^{i-1}+y=N\)
因此\(11*10^{i-1}+2y=N-d*10^{i-1}\)
解不定方程即可,然後搞出所有解就行了
用map實現本題十分方便(既可以去重也可以排序)
P.S. 本題的輸出十分有意思
code:

#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
ll N,P10[15];
map<ll,ll>ans;
void exgcd(ll a,ll b,ll &g,ll &x,ll &y)
{
	if(!b){x=1;y=0;g=a;return;}
	exgcd(b,a%b,g,x,y);
	ll xx=x;
	x=y;y=xx-a/b*y;
}
inline int ws(ll x)
{
	if(!x)return 1;
	int anss=0;
	for(;P10[anss]<=x;++anss);
	return anss;
}
int main()
{
	P10[0]=1;
	for(int i=1;i<=10;++i)P10[i]=P10[i-1]*10ll;
	while(scanf("%lld",&N)!=EOF)
	{
		ans.clear();
		int len=ws(N);
		if(N%P10[len-1]==0)ans[N]=0;
		for(int i=1;i<=len;++i)
		{
			ll a=11*P10[i-1],b=2,g=0,x=0,y=0;
			exgcd(a,b,g,x,y);
			ll xx=x,yy=y;
			for(int d=0;d<10;++d)
			{
				x=xx,y=yy;
				ll c=N-1ll*d*P10[i-1];
				if(c%g)continue;
				ll s=c/g,_a=0,_b=0;
				x*=s,y*=s;_a=a/g,_b=b/g;
				y=(y%_a+_a)%_a;
				for(;y<P10[i-1];y+=_a)
				{
					x=(c-y*b)/a;
					ll X=x*P10[i]+d*P10[i-1]+y,Y=x*P10[i-1]+y;
					if(X!=Y&&X>=0&&Y>=0)ans[X]=Y;
				}
			}
		}
		printf("%lld\n",ans.size());
		for(map<ll,ll>::iterator it=ans.begin();it!=ans.end();it++)
			printf("%lld + %0*lld = %lld\n",it->first,ws(it->first)-1,it->second,N);
	}
	return 0;
}

最小公倍數的最小和

注意細節:
1.n=1
2.n為素數
3.n=2^31-1(答案會越界)

#include<bits/stdc++.h>
using namespace std;
int n,kase;
int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		long long ans=0;int nn=n,mx=(int)sqrt(n);
		for(int i=2;i<=mx;++i)
		{
			int cnt=1;
			while(n%i==0)n/=i,cnt*=i;
			if(cnt!=1)ans+=1ll*cnt;
			if(n==1)break;
		}
		if(ans==0)ans=1ll*nn+1;
		else if(n!=1)ans+=1ll*n;
		if(ans==1ll*nn)++ans;
		if(nn==1)ans=2;
		printf("Case %d: %lld\n",++kase,ans);
	}
	return 0;
}

選擇與除法

質因數分解。統計指數就可以了

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
int pr[N],p,q,r,s;
double ans;
inline int cp(int &x,int d)
{
	int cnt=0;
	while(x%d==0)x/=d,++cnt;
	return cnt;
}
inline void work(int x,int d)
{
	int mx=(int)sqrt(x+1);
	for(int i=2;i<=mx&&x!=1;++i)
		if(x%i==0)pr[i]+=d*cp(x,i);
	if(x>1)pr[x]+=d;
}
inline void ad(int x,int d)
{
	for(int i=2;i<=x;++i)
		work(i,d);
}
inline void solve()
{
	ans=1;
	for(int i=2;i<=N-5;++i)
		ans*=pow(i,pr[i]),pr[i]=0;
	printf("%.5lf\n",ans);
}
int main()
{
	while(scanf("%d%d%d%d",&p,&q,&r,&s)==4)
	{
		ad(p,1);ad(q,-1);
		ad(s,1);ad(r,-1);
		ad(r-s,1);ad(p-q,-1);
		solve();
	}
	return 0;
}

交表

題意:求出整數對個數(x,y)(x,y<=n n<=5e4)使得其最大公約數為1
題解:這樣的整數對除(1,1)其他的x不等於y
不妨設x>y(x<y同理)那麼只要確定x,答案就是phi(x)
篩出所有phi(x),求出字首和即可
code:

#include<bits/stdc++.h>
using namespace std;
const int N=50005;
vector<int>pr;
int phi[N],sm[N],n;
bool flag[N];
inline void pre()
{
	int m=N-5;
	for(int i=2;i<=m;++i)
	{
		if(!flag[i]){phi[i]=i-1;pr.push_back(i);}
		for(int j=0;j<pr.size();++j)
		{
			if(i*pr[j]>m)break;
			flag[i*pr[j]]=true;
			if(i%pr[j])phi[i*pr[j]]=phi[i]*phi[pr[j]];
			else{phi[i*pr[j]]=phi[i]*pr[j];break;}
		}
	}
	for(int i=2;i<=m;++i)
		sm[i]=sm[i-1]+phi[i];
}
int main()
{
	pre();
	while(scanf("%d",&n)==1&&n)
		printf("%d\n",sm[n]*2+1);
	return 0;
}

樹林裡的樹

題解:
可以發現座標軸上有且僅有4個點可以看到,單獨考慮
發現只用考慮右上角的點就行了(其他方位同理)
由於列數較少行數較多,因此列舉列數
對於列x,能看到的當且僅當其縱座標與x互質
不難想到尤拉函式
稍稍擴充套件可以證明\(kx+1-(k+1)x\)(其中k為正整數)中與x互質的數的個數仍然為phi(x)
簡略證明:
若d(d<x)與x互質,那麼d+x仍然與x互質
若e(e<=x)與x不互質,那麼e+x仍然與x不互質
由此可以快速計算每列可以看到多少樹
至於餘數部分,暴力計算統計即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
vector<int>p;
int lp[N],phi[N],a,b;
bool flag[N];
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
inline void pre()
{
	int m=N-5;phi[1]=1;
	for(int i=2;i<=m;++i)
	{
		if(!flag[i]){phi[i]=i-1;p.push_back(i);}
		for(int j=0;j<p.size();++j)
		{
			if(i*p[j]>m)break;
			flag[i*p[j]]=true;
			if(i%p[j])phi[i*p[j]]=phi[i]*phi[p[j]];
			else{phi[i*p[j]]=phi[i]*p[j];break;}
		}
	}
}
int main()
{
	pre();
	while(scanf("%d%d",&a,&b)==2&&a&&b)
	{
		ll ans=0,t=0;
		for(int i=1;i<=a;++i)
		{
			int d=b/i;
			ans+=1ll*d*phi[i];
			for(int j=d*i+1;j<=b;++j)
				if(gcd(j,i)==1)++ans;
		}
		ans*=4;ans+=4;t=1ll*(a*2+1)*(b*2+1)-1;
		printf("%.10lf\n",(double)ans/t);
	}
	return 0;
}