math
T2:
问题描述
给你一个除法表达式:X1/X2/X3/X4……/Xk 其中Xi是正整数并且 Xi <= 2 000 000 000( 1<=i<=k,k<=10 000 )。除法表达式应当按照从左到右的顺序求结果,例如:表达式1/2/1/2的值是1/4,你可以在表达E中嵌入括号改变计算顺序,例如表达式(1/2)/(1/2)的值是1。现在给你一个除法表达式E,计算是否能够通过加括号(或者不加)得到表达式E' ,E'的值为整数。
输入文件
输入数据包括多组数据,每组数据占一行,给出的是题目描述的表达式E,E中不含空格。
输出文件
每组测试数据占一行如果能找到题目中描述的E' 则输出"YES"(不含引号),否则输出"NO" (不含引号)。
样例输入
1/2/1/2
2/3
样例输出
YES
NO
限制与约定
时间限制:1s
空间限制:128MB
思路 :第二个放在分母上,其他的都可以放在分子上。。。超int。。。
std:
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int maxn=1e4+5; char ch[maxn*15]; int a[maxn],temp,k; inline int read(){ int a=0; while(ch[temp]<'0'||'9'<ch[temp])temp++; while('0'<=ch[temp]&&ch[temp]<='9'){ a=(a<<1)+(a<<3)+ch[temp++]-'0'; } return a; } int main(){ while(scanf("%s",ch+1)>0){ int len=strlen(ch+1); temp=1;k=0; for(int i=1;ch[temp]!='\0';i++,k++){ a[i]=read(); //printf("%d ",a[i]); } for(int i=1;i<=k;i++){ if(i!=2){ int d=__gcd(a[i],a[2]);//使用gcd约分,注意别超int。。。 a[2]/=d;//第二个数一定是分母 } } printf(a[2]==1?"YES\n":"NO\n"); } return 0; }
组合数:
使用阶乘以及逆元的运算:
std:
#define LL long long #include<cstdio> #include<algorithm> using namespace std; const int P=1000000007; const int maxn=1000000+5; int n,a,b,x,y,d; void exgcd(int a,int b){ if(!b){ d=a;x=a;y=0; return; } exgcd(b,a%b);//bx+(a-qb)y=0 -> ay+b(x-qy)=0; int t=y; y=(x-(a/b)*y); x=t; } inline int get_inv(int a){ exgcd(a,P);//注意顺序 return (x%P+P)%P; }
//以上是求解逆元的 拓展欧几里得算法 int inv[maxn],jc[maxn],njc[maxn]; inline void inv_init(){ inv[1]=jc[1]=njc[1]=1; for(int i=2;i<=n;i++){ inv[i]=1ll*(P-P/i)*inv[P%i]%P;//当p为质数时成立的递推公式 jc[i]=1ll*jc[i-1]*i%P; njc[i]=1ll*njc[i-1]*inv[i]%P;// } } inline LL C(int m,int n){ return 1ll*jc[n]%P*njc[n-m]%P*njc[m]%P; } int main(){ scanf("%d%d%d",&n,&a,&b); inv_init(); printf("%lld",C(min(a,b), return 0; }
eular 函数:
#include<cstdio> using namespace std; const int maxn=1000000+5; int phi[maxn],n; inline void get_phi(){ for(int i=1;i<=n;i++)phi[i]=i; for(int i=2;i<=n;i++){ if(phi[i]==i){ for(int j=1;j*i<=n;j++){ phi[j*i]=phi[j*i]/i*(i-1);//埃氏筛,较慢 } } } return; } inline void int main(){ scanf("%d",&n); get_phi(); for(int i=1;i<=n;i++){ printf("%d\n",phi[i]); } return 0; }
线性筛(eular 筛)
std:
#include<cstdio> using namespace std; const int maxn=1000000+5; int phi[maxn],n,cnt,prime[maxn]; bool flag[maxn]; inline void get_phi(){ for(int i=1;i<=n;i++)phi[i]=i; for(int i=2;i<=n;i++){ if(phi[i]==i){ for(int j=1;j*i<=n;j++){ phi[j*i]=phi[j*i]/i*(i-1); } } } return; } inline void phi_init(){ /*for(int i=1;i<=n;i++){ phi[i]=i; }*/ phi[1]=1; for(int i=2;i<=n;i++){ if(!flag[i]){//is prime prime[++cnt]=i; phi[i]=i-1; for(int j=1;j<=cnt;j++){ int t=prime[j]*i; if(t>n)break; flag[t]=1; phi[t]=phi[i]*( (j==cnt) ? prime[j] : (prime[j]-1) );//积性函数 } } else{ for(int j=1;j<=cnt;j++){ int t=prime[j]*i; if(t>n)break; flag[t]=1; if(i%prime[j]==0){//eular 定理推论 phi[t]=phi[i]*prime[j]; break; } else{ phi[t]=phi[i]*(prime[j]-1); } } } } } int main(){ scanf("%d",&n); phi_init(); for(int i=1;i<=n;i++){ printf("%d\n",phi[i]); } return 0; }
273. 古代猪文(BZOJ1951)
问题描述
猪王国的文明源远流长,博大精深。 iPig在大肥猪学校图书馆中查阅资料,得知远古时期猪文文字总个数为N。当然,一种语言如果字数很多,字典也相应会很大。当时的猪王国国王考虑到如果修一本字典,规模有可能远远超过康熙字典,花费的猪力、物力将难以估量。故考虑再三没有进行这一项劳猪伤财之举。当然,猪王国的文字后来随着历史变迁逐渐进行了简化,去掉了一些不常用的字。 iPig打算研究古时某个朝代的猪文文字。根据相关文献记载,那个朝代流传的猪文文字恰好为远古时期的k分之一,其中k是N的一个正约数(可以是1和N)。不过具体是哪k分之一,以及k是多少,由于历史过于久远,已经无从考证了。 iPig觉得只要符合文献,每一种能整除N的k都是有可能的。他打算考虑到所有可能的k。显然当k等于某个定值时,该朝的猪文文字个数为N / k。然而从N个文字中保留下N / k个的情况也是相当多的。iPig预计,如果所有可能的k的所有情况数加起来为P的话,那么他研究古代文字的代价将会是G的P次方。 现在他想知道猪王国研究古代文字的代价是多少。由于iPig觉得这个数字可能是天文数字,所以你只需要告诉他答案除以999911659的余数就可以了。
输入数据
有且仅有一行:两个数N、G,用一个空格分开。
输出数据
有且仅有一行:一个数,表示答案除以999911659的余数。(无行末回车)。
样例输入1
4 2
样例输出1
2048
样例输入2
6 3
样例输出2
426896497
限制与约定
10%的数据中,1 <= N <= 50;
20%的数据中,1 <= N <= 1000;
40%的数据中,1 <= N <= 100000;
100%的数据中,1 <= G <= 1000000000,1 <= N <= 1000000000。
时间限制:1s
空间限制:64MB
wa:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template <typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const LL P=999911659; const LL phi=999911659-1; LL p[5]={2,3,4679,35617},jc[5][40000],njc[5][40000],inv[5][40000]; LL n,g,pp; inline LL quickpow(LL a,LL n){//a^n % P LL ans=1ll; while(n){ if(n&1)ans=ans*a%P; a=a*a%P; n>>=1; } return ans; } inline void com_init(int i){ jc[i][1]=njc[i][1]=inv[i][1]=1ll; for(int j=2;j<=p[i];j++){//递推 预处理 inv[i][j]=( p[i] - p[i] / j )*inv[i][ p[i] % j ]%p[i]; njc[i][j]=njc[i][j-1]*inv[i][j]%p[i]; jc[i][j]=jc[i][j-1]*j%p[i]; } } inline LL c(LL n,LL m,int i){ return jc[i][n]*njc[i][m]%p[i]*njc[i][n-m]%p[i]; }
/*
lucas
*/ LL lucas(LL n,LL m,int i){ if(n<=p[i]&&m<=p[i])return c(n,m,i); else return 1ll*c(n%p[i],m%p[i],i)*lucas(n/p[i],m/p[i],i); } LL x,y; inline void exgcd(LL a,LL b){ if(!b){ x=1; y=0; return; } exgcd(b,a%b); LL temp=y; y=x-a/b*y; x=temp; } inline LL get_inv(LL a,LL p){ exgcd(a,p); return (x%p+p)%p; } LL Mi[5],nMi[5]; inline void CRT_init(){ for(int i=0;i<4;i++){ Mi[i]=phi/p[i]; nMi[i]=get_inv(Mi[i],p[i]); } } inline LL CRT(LL n,LL m){ LL ans=0ll; for(int i=0;i<4;i++){ ans+=lucas(n,m,i)*Mi[i]%phi*nMi[i]%phi; } return ans; } int main(){ read(n);read(g);//write(n);write(g); for(int i=0;i<4;i++)com_init(i); CRT_init(); for(LL i=1;i*i<=n;i++){//求 sum( C(n,k) , k|n ) if(n%i==0){ pp=(pp+CRT(n,i)+CRT(n,n/i))%phi; } } write(pp); //write(quickpow(g%P,pp)); return 0; }
?:
lucas+CRT+Eular: #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template <typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const LL P=999911659; const LL phi=999911659-1; LL p[5]={2,3,4679,35617},jc[5][40000],njc[5][40000],inv[5][40000]; LL n,g,pp; inline LL quickpow(LL a,LL n){//a^n % P LL ans=1ll; while(n){ if(n&1)ans=ans*a%P; a=a*a%P; n>>=1; } return ans; } inline void com_init(int i){ jc[i][1]=njc[i][1]=inv[i][1]=1ll; for(int j=2;j<=p[i];j++){//递推 预处理 inv[i][j]=( p[i] - p[i] / j )*inv[i][ p[i] % j ]%p[i]; njc[i][j]=njc[i][j-1]*inv[i][j]%p[i]; jc[i][j]=jc[i][j-1]*j%p[i]; } } inline LL c(LL n,LL m,int i){ return jc[i][n]*njc[i][m]%p[i]*njc[i][n-m]%p[i]; } LL lucas(LL n,LL m,int i){ if(n<=p[i]&&m<=p[i])return c(n,m,i); else return 1ll*c(n%p[i],m%p[i],i)*lucas(n/p[i],m/p[i],i); } LL x,y; inline void exgcd(LL a,LL b){ if(!b){ x=1; y=0; return; } exgcd(b,a%b); LL temp=y; y=x-a/b*y; x=temp; } inline LL get_inv(LL a,LL p){ exgcd(a,p); return (x%p+p)%p; } LL Mi[5],nMi[5]; inline void CRT_init(){ for(int i=0;i<4;i++){ Mi[i]=phi/p[i]; nMi[i]=get_inv(Mi[i],p[i]); } } inline LL CRT(LL n,LL m){ LL ans=0ll; for(int i=0;i<4;i++){ ans+=lucas(n,m,i)*Mi[i]%phi*nMi[i]%phi;//运算顺序先后。。。 } return ans; } int main(){ read(n);read(g);//write(n);write(g); for(int i=0;i<4;i++)com_init(i); CRT_init(); for(LL i=1;i*i<=n;i++){//求 sum( C(n,k) , k|n ) if(n%i==0){ pp=(pp+CRT(n,i))%phi; if(i*i!=n) pp=(pp+CRT(n,n/i))%phi; //pp=(pp+CRT(n,i)+CRT(n,n/i))%phi; } } write(pp); //write(quickpow(g%P,pp)); return 0; }
AC:但洛谷过不去?应该特判???
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template <typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const LL P=999911659; const LL phi=999911659-1; LL p[5]={2,3,4679,35617},jc[5][40000],njc[5][40000],inv[5][40000]; LL n,g,pp; inline LL quickpow(LL a,LL n){//a^n % P LL ans=1ll; while(n){ if(n&1)ans=ans*a%P; a=a*a%P; n>>=1; } return ans; } inline void com_init(int i){ jc[i][0]=njc[i][0]=inv[i][0]=jc[i][1]=njc[i][1]=inv[i][1]=1ll; //0的阶乘及逆元都会用到,初始化为1 for(int j=2;j<=p[i];j++){//递推 预处理 inv[i][j]=( p[i] - p[i] / j )*inv[i][ p[i] % j ]%p[i]; njc[i][j]=njc[i][j-1]*inv[i][j]%p[i]; jc[i][j]=jc[i][j-1]*j%p[i]; } } inline LL c(LL n,LL m,int i){ return jc[i][n]*njc[i][m]%p[i]*njc[i][n-m]%p[i]; } LL lucas(LL n,LL m,int i){ if(n<p[i] && m<p[i])return c(n,m,i); else return 1ll*c(n%p[i],m%p[i],i)*lucas(n/p[i],m/p[i],i); } LL x,y; inline void exgcd(LL a,LL b){ if(!b){ x=1; y=0; return; } exgcd(b,a%b); LL temp=y; y=x-a/b*y; x=temp; } inline LL get_inv(LL a,LL p){ exgcd(a,p); return (x%p+p)%p; } LL Mi[5],nMi[5]; inline void CRT_init(){ for(int i=0;i<4;i++){ Mi[i]=phi/p[i]; nMi[i]=get_inv(Mi[i],p[i]); } } inline LL CRT(LL n,LL m){ LL ans=0ll; for(int i=0;i<4;i++){ ans=(ans+lucas(n,m,i)*Mi[i]%phi*nMi[i])%phi; } return ans; } int main(){ read(n);read(g); for(int i=0;i<4;i++)com_init(i); CRT_init(); for(LL i=1;i*i<=n;i++){//求 sum( C(n,k) , k|n ) if(n%i==0){ pp=(pp+CRT(n,i))%phi; if(i*i!=n) pp=(pp+CRT(n,n/i))%phi; } } write(quickpow(g%P,pp)); return 0; }
std:
notice the detail:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template <typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const LL P=999911659; const LL phi=999911659-1; LL p[5]={2,3,4679,35617},jc[5][40000],njc[5][40000],inv[5][40000]; LL n,g,pp; inline LL quickpow(LL a,LL n){//a^n % P LL ans=1ll%P; while(n){ if(n&1)ans=ans*a%P; a=a*a%P; n>>=1; } return ans; } inline void com_init(int i){ jc[i][0]=njc[i][0]=jc[i][1]=njc[i][1]=inv[i][1]=1ll%P; for(int j=2;j<=p[i];j++){//递推 预处理 inv[i][j]=( p[i] - p[i] / j )*inv[i][ p[i] % j ]%p[i]; njc[i][j]=njc[i][j-1]*inv[i][j]%p[i]; jc[i][j]=jc[i][j-1]*j%p[i]; } } inline LL c(LL n,LL m,int i){ if(n==m || !m)return 1ll; return jc[i][n]*njc[i][m]%p[i]*njc[i][n-m]%p[i]; }
/*
!!!lucas绝对不能这么写!!!
LL Lucas(LL n,LL m,LL p)//Lucas定理
{
if(n<m) return 0;if(!m) return 1;
return Lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;//可能会出现 a>b 但是 a%p < b%p
}
*/
/*
lucas还能这么写
inline LL lucas(LL n,LL m,LL p){
/*if(m>n)return 0;
if(!m)return 1;
if(n<p && m<p)return C(n,m,p)%p;
else return 1ll*lucas(n%p,m%p,p)*lucas(n/p,m/p,p)%p;*/
LL ans=1ll;
while(n&&m){
LL N=n%p,M=m%p;
if(N<M)ans=0;
else ans=ans*C(N,M,p)%p;
n/=p;
m/=p;
}
return ans;
}
*/
LL lucas(LL n,LL m,int i){ if(m>n)return 0; if(n<=p[i] && m<=p[i])return c(n,m,i)%p[i]; else return 1ll*lucas(n%p[i],m%p[i],i)*lucas(n/p[i],m/p[i],i)%p[i]; } LL x,y; inline void exgcd(LL a,LL b){ if(!b){ x=1; y=0; return; } exgcd(b,a%b); LL temp=y; y=x-a/b*y; x=temp; } inline LL get_inv(LL a,LL p){ exgcd(a,p); return (x%p+p)%p; } LL Mi[5],nMi[5]; inline void CRT_init(){ for(int i=0;i<4;i++){ Mi[i]=phi/p[i]; nMi[i]=get_inv(Mi[i],p[i]); } } inline LL CRT(LL n,LL m){ LL ans=0ll; for(int i=0;i<4;i++){ ans=(ans+lucas(n,m,i)*Mi[i]%phi*nMi[i]%phi)%phi; } return ans; } int main(){ read(n);read(g); if(g==P){ /*!!!Eular定理适用范围:a^phi(m) % m=1 当且仅当(a,m)=1时成立 本题没有说明这个条件,所以即使p时质数也要特判!!! */ write(0); return 0; } for(int i=0;i<4;i++)com_init(i); CRT_init(); for(LL i=1;i*i<=n;i++){//求 sum( C(n,k) , k|n ) if(n%i==0){ pp=(pp+CRT(n,i))%phi; if(i*i!=n) pp=(pp+CRT(n,n/i))%phi; } } write(quickpow(g%P,pp)); return 0; }
268. Saving Beans(HDU3037)
题目描述
虽然冬天很远,松鼠却不得不日夜工作来保存豆子。他们需要足够的食物度过那些漫长的寒冷日子。一段时间后,松鼠家族认为他们必须解决一个问题。他们认为他们可以在不同的树中保存豆子。然而,由于现在食物不够,他们得到的只会是豆子。他们想知道有多少种方法可以在N棵树中保存不超过M个豆子(它们是相同的)。
输入格式
第一行包含一个整数t,表示案例数, 接下来t行,每行包含三个整数N,M,P,这意味着松鼠在N个不同的树中保存的豆子不会超过M。
输出格式
方案数%p
样例输入
2
1 2 5
2 1 5
样例输出
3
3
数据范围
1<=n,m<=10^9,1 < p < 10^5,保证p是素数
时间限制:1s
空间限制:128MB
#259. 无平方因子的个数
问题描述
给出正整数n和m,求区间[n,m]内的“无平方因子”的数有多少个?
整数p无平方因子当且仅当不存在k > 1,使得p是k * k的倍数。
样例输入
1 10
样例输出
7
限制与约定
1 <= n <= m <= 10 ^ 12,m - n <= 10 ^ 7
时间限制:1s
空间限制:128MB
无平方因子的个数 思路分析: 分析最坏情况[1, 10 ^ 12],如果一个个判断的话,显然时间太大,
就算如题意m - n最大值为10 ^ 7,那样也会超时,所以我们考虑其它方法,
即筛选法,考虑这种方法时,我们要把题目中的条件做一下逆向转换,
也就是说对于一个数p,如果其2p*p,3p*p存在与上述范围内
,则他们就不是无平方因子,有没有发现这个和 素数筛法 很像
,Right,我们总结一下,他们都有一个条件,这个条件我们把它称为正条件
,我们必须能够转化出逆条件。 1.这个逆条件以某些数为起始,这些起始数一定符合题目条件。 2.以这些起始数为基底,扩展出不符合的数,将其删除就可以。 3.也就是说拓展出的这些数就是题目所不要的对立面的数。
如我们用无平方因子的数,拓展有平方因子的数。 4.用数学归纳法证明一片,看是否有错。 所以我们可以枚举数p的倍数,2 *p*p.....k *p *p,
在进一步这些无平方因子的数必是素数,所以我们可以以素数为基底,拓展有平方因子的数。
40:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; } while(a); while(temp)putchar(C[temp--]); } LL n,m; const int maxn=1e7+5; bool flag[maxn],isprime[maxn/10]; int cnt,prime[maxn/10]; int main(){ read(n);read(m); int end=sqrt(m); for(int i=2;i<=end;i++){ if(isprime[i])continue; for(int j=i*i;j>0&&j<=end;j+=i)isprime[j]=1; LL j=i*i; for(LL k=n;k<n+j&&k<=m;k++){ if(k%j==0){ for(LL p=k;p<=m;p+=j){ if(!flag[p-n])flag[p-n]=1,cnt++; } break; } } } write(m-n+1-cnt); return 0; }
70:?
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; } while(a); while(temp)putchar(C[temp--]); } LL n,m; const int maxn=1e7+5; bool flag[maxn],isprime[maxn/10]; int cnt; int main(){ read(n);read(m); int end=sqrt(m); for(int i=2;i<=end;i++){ if(isprime[i])continue;//判断在2~sqrt(m)之内的所有prime for(int j=i*i;j>0&&j<=end;j+=i)isprime[j]=1;//第一次进入for可能会越界 LL j=i*i;//注意int*int=int。。。(无数次错误。。。) for(LL k=n/j*j;k<=m;k+=j){ if(k>=n){ if(!flag[k-n])flag[k-n]=1,cnt++; } } } write(m-n+1-cnt); return 0; }
ac:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; } while(a); while(temp)putchar(C[temp--]); } LL n,m; const int maxn=1e7+5; bool flag[maxn],isprime[maxn]; int cnt; int main(){ read(n);read(m); int end=sqrt(m); for(LL i=2;i<=end;i++){ if(isprime[i])continue; LL j=i*i;//数据类型??? while(j<=end){//for的智障型错误 isprime[j]=1; j+=i; } j=i*i; for(LL k=n/j*j;k<=m;k+=j){ if(k>=n){ if(!flag[k-n]){ flag[k-n]=1; cnt++; } } } } write(m-n+1-cnt); return 0; }
#262. 大于公约数的数
问题描述
给定正整数N,M求gcd(i,N)>=M的i的个数。(1<=i<=N )
输入文件
第一行一个整数T(<=100) 代表测试数据的组数。接下来T行,每行一组测试数据,每组数据两个数字N和M(2<=N<=1000000000, 1<=M<=N)。
输出文件
T行,每组测试数据输出一个数字
样例输入
3
1 1
10 2
10000 72
样例输出
1
6
260
限制与约定
时间限制:1s
空间限制:128MB
好题,gcd(i,n)>=m,i属于【1,n】 暴力时间复杂度过高,我们想怎么才能快速的求出符合条件的解, 我们发现,m实际上是一个没有实际意义的数,m+1 or m-1可能会得到一样的答案 我们逆向思考,如果我们知道了符合条件的d,那么有多少个i呢 这显然可以得到gcd(i/d,n/d)=1,也就是说,当d>=m时,n/d的eular函数值 就是符合条件的解, 那我们证明存在性和唯一性 证: 存在性: 存在一个数i s.t. gcd(i,n)>=m那么根据算法一定会找到这个数 证毕, 证: 唯一性: 设有一个数i s.t. gcd(i,n)>=m但是 会被统计>=2次 根据算法,我们是在(i,n)==1时统计的 i,所以,假设会被统计>=2次 就会有>=2 个符合条件的 d ,与(i,n)有且只有一个矛盾、 证毕 所以算法如下: 枚举n的因子,找到>=m,令其为d,加和 phi(n/d)
与gcd有关的统计问题,考虑eular函数
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #define LL long long using namespace std; template<typename T > inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } /*inline void Eular(int n){ for(int i=2;i<=n;i++){ if(!ip[i]){ pr[cnt++]=i; write(i); putchar(','); } for(int j=1;j<=cnt;j++){ LL t=pr[j]*i; if(t>n)break; ip[t]=1; if(i%pr[j]==0)break; } } }*/ inline int phi(int n){ int ans=n,num=n,i=2; while(num!=1 && i*i<=n){ if(num%i==0){ ans=ans/i*(i-1); while(num%i==0)num/=i; } i++; } if(num!=1)ans=ans/num*(num-1); return ans; } const int maxn=1000000; int t,n,m; int main(){ //freopen("1.out","w",stdout); read(t); while(t--){ read(n);read(m); int i=1,ans=0; while(i*i<=n){ if(n%i==0){ if(i*i==n)ans+=( i>=m ? phi(i) : 0 ); else ans+=( i>=m ? phi(n/i) : 0)+( (n/i)>=m ? phi(i) : 0 ); } i++; } write(ans);putchar('\n'); } return 0; }
272. Gcd(BZOJ2818)
题目描述
给定整数N,求1<=x,y<=N且x,y的最大公约数为素数的数对(x,y)有多少对。
输入文件
一个整数N
输出文件
如题
样例输入
4
样例输出
4
限制与约定
对于10%的数据,N<=10^3
对于20%的数据,N<=10^4
对于40%的数据,N<=10^5
对于60%的数据,N<=10^6
对于100%的数据, N<=10^7
时间限制:1000ms
空间限制:128MB
分析题目, 和gcd有关的统计问题,首先想到Eular函数, 那我们怎么使用呢? 求出1~n内gcd=p的数对个数 我们发现 筛法可以求出所有素数,所以我们枚举每一个素数,考虑1~n内所有可能符合条件的解, 一共有(n/p)个,我们可以用Eular了,求出这(n/p)个数的phi值并相加,就会得到 (x,y)=p & x>y 的数对个数,然后反过来 容斥,就能得到,gcd(x,y)=p的 解的个数 时间复杂度O(n)可以接受
#include<cstdio> #include<algorithm> #include<cstdlib> #include<iostream> #include<cmath> #include<cstring> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ putchar('-'); a=-a; } do{ C[++temp]=a%10+'0'; a/=10; }while(a); while(temp)putchar(C[temp--]); } const int maxn=1e7+5; int phi[maxn],pr[maxn],cnt; LL sumphi[maxn]; inline void Eular(int n){ for(int i=1;i<=n;i++)phi[i]=i;sumphi[1]=1ll; for(int i=2;i<=n;i++){ if(phi[i]==i){ phi[i]=i-1; pr[++cnt]=i; } for(int j=1;j<=cnt;j++){ LL t=pr[j]*i; if(t>n)break; if(i%pr[j]==0){ phi[t]=phi[i]*pr[j]; break; } else{ phi[t]=phi[i]*(pr[j]-1); } } sumphi[i]=sumphi[i-1]+phi[i]; } } int n; LL ans; int main(){ read(n); Eular(n); for(int j=1;j<=cnt;j++){ ans+=(sumphi[ n / pr[j] ]<<1)-1;//去重,(p,p)= p,会重复计算2遍 } write(ans); return 0; }
拓展欧拉定理:
a^c=a^(c%phi(m)),(a,m)==1
a^(c%phi(m)+phi(m)),(a,m)!=1 {c>phi(m)时降次}
这个定理证明比较繁琐,用于幂数过大时降次使用
260. 无关的元素
题目描述
对于给定的n个数a1,a2,...,an,依次求出相邻两数之和,将得到一个新数列。重复上述操作,最后结果将变成一个数。问这个数除以m的余数与哪些数无关?
例如n=3,m=2时,第一次求和得到a1+a2,a2+a3,再次求和得到a1+2a2+a3,它除以2的余数和a2无关。
输入文件
第1行:2个整数n和m(1<=n<=10^5, 2 <=m<=10^9)
输出文件
按升序列出与m无关的元素的序号,每行1个。 若与全部元素无关,输出0
样例输入
5 3
样例输出
3
限制与约定
时间限制:1s
空间限制:128MB
WA:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<iostream> #define LL long long using namespace std; template<typename T> inline void read(T &a){ a=0;bool b=0;char x=getchar(); while(x<'0'||'9'<x){ if(x=='-')b=1; x=getchar(); } while('0'<=x&&x<='9'){ a=(a<<1)+(a<<3)+x-'0'; x=getchar(); } if(b)a=-a; } char C[50]; int temp; template<typename T> inline void write(T a){ if(a<0){ a=-a; putchar('-'); } do{ C[++temp]=a%10+'0'; a/=10; } while(a); while(temp)putchar(C[temp--]); } const int maxn=1e5+5; int prime[maxn],p; bool isprime[maxn]; inline void Euler(int n){ for(int i=2;i<=n;i++){ if(!isprime[i])prime[++p]=i; for(int j=1;j<=p;j++){ LL t=(LL)prime[j]*i; if(t>n)break; isprime[t]=1; if(i%prime[j]);else break; } } } /*inline void print(){ for(int i=1;i<=p;i++){ printf("%d:%d ",prime[i],factor[prime[i]]); } }*/ int n,m,cnt,factor[maxn];//cnt分母有几种质数 inline void add_factor(int num){ int i=1; int k=num; while(prime[i]*prime[i]<=num && k!=1){ while(!(k%prime[i])){ k/=prime[i]; factor[prime[i]]++; if(!factor[prime[i]])cnt--; } i++; } if(k!=1){ factor[k]++; if(!factor[k])cnt--; } } inline void del_factor(int num){ int i=1; int k=num; while(prime[i]*prime[i]<=num && k!=1){ while(!(k%prime[i])){ k/=prime[i]; factor[prime[i]]--; if(factor[prime[i]]==-1)cnt++; } i++; } if(k!=1){ if(k>n){ write(0); exit(0); } else{ factor[k]--; if(factor[k]==-1)cnt++; } } } int main() { read(n);read(m); n--; Euler(n); del_factor(m); for(int i=0;i<=n;i++){ if(cnt<=0){ write(i+1); putchar('\n'); } if(i<n){ add_factor(n-i); del_factor(i+1); } } return 0;
}