Bi-shoe and phi-shoe 知识点:欧拉函数打表
题意:竹子的得分为它长度的欧拉函数值,Bi-shoe想买竹子给同学,每个同学收到的竹子得分>=他的幸运数字,竹子每单位长度需要花1Xukha。问Bi-shoe最少花多少钱?
思路:欧拉值打表,遍历
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e4+10;
const int M=1e6+10;
int euler[M];
int a[N];
void gete()
{
for(int i=1;i<=M;++i)
euler[i]=i;
for(int i=2;i<=M;++i)
{
if(euler[i]==i) //说明是素数
for(int j=i;j<=M;j+=i) //素数的倍数的euler[j]一并改掉
euler[j]=euler[j]/i*(i-1); //euler[j]=euler[j]*(1-1/i)
}
/*for(int i=2;i<=10;++i)
cout<<i<<' '<<euler[i]<<endl;*/
}
int main()
{
gete();
int T;
cin>>T;
for(int k=1;k<=T;k++)
{
int n;
cin>>n;
long long ans=0;
for(int i=1;i<=n;++i)
cin>>a[i];
sort(a+1,a+1+n);
for(int i=1,j=2;i<=n&&j<=M;j++)
{
if(euler[j]>=a[i])
{
ans+=j;
i++;
j--;
}
}
cout<<"Case "<<k<<": "<<ans<<' '<<"Xukha"<<endl;
}
return 0;
}
Sigma Function 知识点:规律
记得当时看了很多推导,后悔当时没有写博客的习惯。
题意:1~n中有多少个数,其因子和为偶数
规律:因子和为奇数的有:平方数以及平方数的二倍。总数减去为奇数的就是偶数的
智商不够就暴力打表找规律叭_(:з」∠)_
#include<iostream>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long LL;
int main()
{
int T;
cin>>T;
for(int i=1;i<=T;++i)
{
LL n;
cin>>n;
LL ans=(LL)sqrt(n)+(LL)sqrt(n/2); //中间不强制转换的话会wa
printf("Case %d: %lld\n",i,n-ans);
}
return 0;
}
Leading and Trailing 知识点:快速模指数算法 指数问题转成对数求解
题意:求n^k的前三位和后三位
思路:后三位很好求,只要模1000就能求出,关键是前三位
前三位的求法是这样的,将用科学计数法表示,那么 前三位即取整
我们将a用表示,由于科学计数法中a<10,所以一定是小数,即
//A
#include<stdio.h>
#include<iostream>
#include<math.h>
using namespace std;
typedef long long ll;
const int mod=1000;
ll quick_mod(ll a,ll k)
{
if(k==0) return 1;
ll ans=1;
while(k)
{
if(k&1)
ans=ans*a%mod;
a=a*a%mod;
k=k>>1;
}
return ans;
}
int main()
{
int T;
cin>>T;
for(int kase=1;kase<=T;kase++)
{
ll n,k;
cin>>n>>k;
ll trail=quick_mod(n,k); //只要模1000就能算出最后三位
double x=k*log10(n)-(int)(k*log10(n)); //强制转换要加括号
double tmp=pow(10,x);
int lead=(int)(tmp*100);
printf("Case %d: %d %03d\n",kase,lead,trail);
}
return 0;
}
Goldbach`s Conjecture 知识点:素数打表
题意:给一个数n,问能找到多少素数对,其和是n
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
const int N=1e7+10;
bool tag[N];
int prime[1000000];
int cnt=0;
void getprime()
{
memset(tag,false,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2;i<=N;++i)
{
if(!tag[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&prime[j]*i<=N;++j)
{
tag[prime[j]*i]=true;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
getprime();
int T;
cin>>T;
for(int k=1;k<=T;++k)
{
int n;
cin>>n;
int ans=0;
//cout<<"keke"<<endl;
for(int i=1;i<=cnt&&2*prime[i]<=n;++i)
{
// cout<<"keke"<<tag[n-prime[i]]<<endl;
if(tag[n-prime[i]]==false)
ans++;
}
printf("Case %d: %d\n",k,ans);
}
return 0;
}
用正确方法求h(n)
#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long LL;
LL h(LL n)
{
LL res=0;
LL i=1;
for(;i*(i+1)<n;++i)
{
LL num=n/i-n/(i+1);
//cout<<i<<":"<<num<<endl;
res+=num*i;
}
// cout<<i<<endl;
// cout<<res<<endl;
LL k=n/i;
for(LL j=1;j<=k;++j)
res+=n/j;
return res;
}
int main()
{
// freopen("in.txt","r",stdin);
int T;
cin>>T;
for(int kase=1;kase<=T;kase++)
{
LL n;
cin>>n;
cout<<"Case "<<kase<<": "<<h(n)<<endl;
}
}
题意:求1 + 1/2 + 1/3 + 1/4 + 1/ 5 +...+ 1/ n
思路:有公式,记不住咋办?将数分组存储的思路
#include<iostream>
#include<stdio.h>
using namespace std;
const int N=1e8+10;
const int NN=1e8/40+10;
//每隔四十记录一次答案 不然空间不够,这个样子的话循环一次 1s 之后每个数进来最多循环39次
double a[NN];
void pre()
{
double sum=0;
for(int i=1;i<1e8+10;++i)
{
sum+=(1.0)/i;
if(i%40==0) a[i/40]=sum;
}
}
int main()
{
//cout<<NN<<endl;
pre();
int T;
cin>>T;
int cas=0;
while(T--)
{
++cas;
int n;
cin>>n;
int x=n/40; //将数锁定在一个范围,机智
double ans=a[x];
for(int i=x*40+1;i<=n;++i)
ans+=1.0/i;
printf("Case %d: %.10f\n",cas,ans);
}
return 0;
}
题意: 给出,求最大的
这个y的最大值就是答案
显然的时候最大
这题的坑点在于x可能是负的,我们在处理的时候把它转成正数去做的,如果y是偶数就会导致最终解为正数,所以要把一直除二,除到奇数为止
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
typedef long long LL;
const int N=1e6+100;
bool tag[N];
int prime[N];
int cnt;
void getprime()
{
memset(tag,0,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2;i<N;++i)
{
if(!tag[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&prime[j]*i<N;++j)
{
tag[prime[j]*i]=1;
if(i%prime[j]==0)
break;
}
}
// for(int i=1;i<=100;++i)
// cout<<prime[i]<<endl;
// cout<<cnt<<" "<<prime[cnt]<<endl; 78504 1000099
}
int gcd(int a,int b)
{
return a%b==0?b:gcd(b,a%b);
}
int solve(LL n)
{
int flag=0;
if(n<0)
{
n=-n;
flag=1;
}
int ans=0;
int i;
for(i=1;i<=cnt&&prime[i]*prime[i]<=n;++i)
{
if(n%prime[i]==0)
{
while(n%prime[i]==0)
{
ans++;
n/=prime[i];
}
break;
}
}//得到第一个整除的素因子
if(ans==0) return 1; //这句话不能少
else
{
++i;
for(;i<=cnt&&prime[i]*prime[i]<=n;++i)
{
int e=0;
if(n%prime[i]==0)
{
while(n%prime[i]==0)
{
e++;
n/=prime[i];
}
ans=gcd(ans,e);//我他妈的把这句放在外面了,导致如果n%prime[i]!=0,ans=gcd(ans,0)
}
}
if(flag) //n是负数
{
while(ans%2==0)
ans=ans>>1;
}
}
return ans;
}
int main()
{
getprime();
int T;
cin>>T;
for(int kase=1;kase<=T;++kase)
{
LL x; //不用LL的话会爆int有符号最大(2^16)
cin>>x;
cout<<"Case "<<kase<<": "<<solve(x)<<endl;
}
return 0;
}
//1073741824=2^30
//re 当9223372036854775807时 returned 0xC0000094 6位正数/负数时 最好的debug方式是先拿一个简单的不符合要求的数跑一下
题意:b是32位有符号整数,判读a是否能整除b
数论的魅力在于:本来以为这道题必须要用大数来做了,but!
如果a<0,先把a转化为绝对值
#include<iostream>
#include<string>
using namespace std;
typedef long long LL;
int main()
{
int T;
cin>>T;
for(int kase=1;kase<=T;++kase)
{
string a;
LL b,ans=0;
cin>>a>>b;
if(b<0) b=-b;
int len=a.size();
int i=0;
if(a[0]!='-')
{
ans=a[0]-'0';
i=1;
}
else
{
ans=a[1]-'0';
i=2;
}
for(;i<len;++i)
ans=(ans*10+a[i]-'0')%b;
if(ans==0)
cout<<"Case "<<kase<<": "<<"divisible"<<endl;
else
cout<<"Case "<<kase<<": "<<"not divisible"<<endl;
}
return 0;
}
题意:[a,b]区间内有多少素数 32位数10000以内区间长度判断素数个数
思路:数量太大无法打表,策略是把[a,b]区间->[0,b-a]区间,用素数筛在这个区间范围内筛素数
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int N=1e6+10;
const int M=2e6;
typedef long long LL;
int tag[N];
LL prime[M];
int interval[100010];
int cnt;
LL a,b;
void getp()
{
memset(tag,0,sizeof(tag));
tag[0]=tag[1]=1;
for(int i=2;i<N;++i)
{
if(!tag[i])
prime[++cnt]=i;
for(int j=1;j<=cnt&&(LL)(i*prime[j])<N;++j)
{
tag[(LL)(i*prime[j])]=1;
if(i%prime[j]==0) break;
}
}
}
int solve(LL a,LL b)
{
memset(interval,0,sizeof(interval));
for(int j=1;j<=cnt;++j)
{
if((LL)(prime[j]*prime[j])>b) break; //用所有平方值小于b的prime去更新那个区间
LL s=(a+prime[j]-1)/prime[j]; //计算第一个比a大的prime[j]的倍数是几倍 (向上取整的好操作)
if(s<2) s=2; //避免把这个素数当成合数筛掉
s*=prime[j];
for(;s<=b;s+=prime[j])
interval[s-a]=1; //把集合整到[0,b-a]
}
int ans=0;
for(int i=a;i<=b;++i)
{
if(!interval[i-a])
++ans;
}
return ans;
}
int main()
{
getp();
int T;
cin>>T;
int kase=0;
while(T--)
{
++kase;
cin>>a>>b;
int ans=solve(a,b);
if(a==1) --ans; //a=1的话在interval里面没有被筛掉
printf("Case %d: %d\n",kase,ans);
}
return 0;
}
题意:给出q,找出n,使得n!的十进制表达最后有q个0
题目的思路很简单,首先n!里面2和5搭配形成10,5的数目比2多,所以有几个5就能有几个10
我记得我是想暴力求出所有情况(打表),但是忽略了q是1e8的量级,这种涉及很大很大的数要用二分法啊
#include<iostream>
#include<map>
using namespace std;
const int N=1e8+10;
const int INF=0x3f3f3f3f;
typedef long long LL;
int Q;
LL solve(LL n) //计算n!里有几个5
{
LL res=0;
while(n)
{
res+=n/5; //如果是算n里面有几个5,每n/=5 加1 ,注意这个区别
n/=5;
}
return res;
}
LL erfen(LL l,LL r)
{
LL res=-1;
while(l<=r)
{
LL mid=(l+r)/2;
if(solve(mid)==Q)
{
res=mid;
r=mid-1;
}
else if(solve(mid)>Q)
r=mid-1;
else
l=mid+1;
}
return res;
}
int main()
{
// cout<<INF<<endl;
int T;
cin>>T;
for(int kase=1;kase<=T;++kase)
{
cin>>Q;
LL left=1,right=INF;
LL ans=erfen(left,right);
if(ans==-1)
cout<<"Case "<<kase<<": "<<"impossible"<<endl;
else
cout<<"Case "<<kase<<": "<<ans<<endl;
}
return 0;
}
题意:求出1~n之间所有数对的最大公约数之和
脑子抽了才会想去暴力。。。
设
则
得到这个递推式后,问题转化为如何求,当然是暴力遍历啦
设表示满足的个数,则
又,所以与n的最大公约数是i的数有个,因此呼啦!最终就变成求欧拉函数了
这道题以及以往做过的数论题提醒我,筛法的思想很重要,特征是一个数由其所有约数更新,用筛法最省时(这种好像叫填表法)
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=4e6+10;
typedef long long LL;
int euler[N];
LL F[N];
LL G[N];
void geteu()
{
for(int i=1;i<N;++i)
euler[i]=i;
for(int i=2;i<N;++i)
{
if(euler[i]==i) //说明他是素数
for(int j=i;j<N;j+=i)
euler[j]=euler[j]/i*(i-1);
}
}
int main()
{
geteu();
for(int i=1;i<N;++i)
for(int j=2*i;j<N;j+=i) //类似筛法的思想很重要
F[j]+=euler[j/i]*i;
G[2]=1;
for(int i=3;i<N;++i)
G[i]=G[i-1]+F[i];
// for(int i=2;i<=10;++i)
// cout<<G[i]<<endl;
int n;
while(scanf("%d",&n)&&n)
{
cout<<G[n]<<endl;
}
return 0;
}
//跑不出来原因,我的f(i)计算太慢了 相当于O(n^2)