Lucas定理:3
1.How many integers can you find HDU - 1796
题意: 有一个包含m(m<=10)个数的集合,问小于N的数中,有多少数能被集合中至少一个整数整除。
分析:
N
N
N以内可以整除
x
x
x的数有
(
N
−
1
)
/
x
(N-1)/x
(N−1)/x个;
N
N
N以内可以同时整除
x
x
x和
y
y
y的数有
(
N
−
1
)
/
l
c
m
(
x
,
y
)
(N-1)/lcm(x,y)
(N−1)/lcm(x,y)个;
N
N
N以内可以整除
x
x
x或
y
y
y的数有
(
N
−
1
)
/
x
−
(
N
−
1
)
/
l
c
m
(
x
,
y
)
(N-1)/x-(N-1)/lcm(x,y)
(N−1)/x−(N−1)/lcm(x,y)个
注: / / /表示整除
利用容斥原理与二进制枚举即解
坑点:集合中的数可能含0,不特殊判断会RE
代码:
int a[25];
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int lcm(int a,int b){return a/gcd(a,b)*b;}
int main(){
int N,M;
while(~scanf("%d%d",&N,&M)){
N--;
int ans=0,num=0;
for(int i=0;i<M;i++) {
int temp;
scanf("%d",&temp);
if(temp) a[num++]=temp;
}
for(int i=1;i<(1<<num);i++){
int cnt=0;
int temp=1;
for(int j=0;j<num;j++)
if(i&(1<<j))
cnt++,temp=lcm(temp,a[j]);
if(!temp) continue;
if(cnt%2) ans+=N/temp;
else ans-=N/temp;
}
printf("%d\n",ans);
}
return 0;
}
2.Co-prime HDU - 4135
题意: 求整数 A A A到 B B B中,与 N N N互质的个数
分析:
令 c a l ( X ) cal(X) cal(X)为 1 1 1到 X X X中与 N N N互质的个数,则题目转化为求 c a l ( B ) − c a l ( A − 1 ) cal(B)-cal(A-1) cal(B)−cal(A−1)
与 N N N互质等价于不和N含有相同的质因子
和 N N N不互质的个数更好求:
对 N N N质因子分解,得到 N = p 1 p 2 p 3 ⋅ ⋅ ⋅ p x N=p_1p_2p_3\cdot\cdot\cdot p_x N=p1p2p3⋅⋅⋅px,则 1 1 1到 X X X中有 X / p 1 X/p_1 X/p1个数含因子 p 1 p_1 p1,即不与 N N N互质,然后容斥原理即可
代码:
int Prime[100005];
bool Isprime[100005];
int cnt=0,num=0;
long long a,b,n;
int fact[105];
void get_prime(){
for(int i=2;i<=100000;i++) Isprime[i]=1;
for(int i=2;i<=100000;i++){
if(Isprime[i]) Prime[cnt++]=i;
for(int j=0;j<cnt&&Prime[j]*i<=100000;j++){
Isprime[Prime[j]*i]=0;
if(i%Prime[j]==0) break;
}
}
}
long long cal(long long x){ //1到x中有多少数和n互质
long long ret=x;
for(int i=1;i<(1<<num);i++){
int _cnt=0;
int mul=1;
for(int j=0;j<num;j++)
if(i&(1<<j))
_cnt++,mul*=fact[j];
if(_cnt%2) ret-=x/mul;
else ret+=x/mul;
}
return ret;
}
int main(){
get_prime();
int T;
scanf("%d",&T);
for(int Case=1;Case<=T;Case++){
scanf("%lld%lld%lld",&a,&b,&n);
num=0;
long long temp=n;
for(int i=0;i<cnt&&Prime[i]*Prime[i]<=temp;i++)
if(temp%Prime[i]==0){
fact[num++]=Prime[i];
while(temp%Prime[i]==0)
temp/=Prime[i];
}
if(temp>1) fact[num++]=temp;
printf("Case #%d: %lld\n",Case,cal(b)-cal(a-1));
}
return 0;
}
3.DP? HDU - 3944
题意: 杨辉三角形中,在每个点可以选择向下或向右下走,求从 ( 0 , 0 ) (0,0) (0,0)走到点 ( n , k ) (n,k) (n,k)经过的数字和的最小值。
分析:
从顶点到点 ( n , k ) (n,k) (n,k)需要向下移动 n n n格,向右移动 k k k格,并且只有向右下移动才能实现右移。所以一定会进行 k k k次向右下移动, n − k n-k n−k次向下移动。
由于杨辉三角形的对称性,因此可以只考虑 2 ∗ k < = n 2*k<=n 2∗k<=n的情况下,若 2 ∗ k > n 2*k>n 2∗k>n,则令 k = n − k k=n-k k=n−k
易得,向右下移动这一操作越晚进行越好,所以答案为 n − k + C ( n , k ) + C ( n − 1 , k − 1 ) + … … + C ( n − k + 1 , 1 ) + C ( n − k , 0 ) n-k+C(n,k)+C(n-1,k-1)+……+C(n-k+1,1)+C(n-k,0) n−k+C(n,k)+C(n−1,k−1)+……+C(n−k+1,1)+C(n−k,0)
由于 C ( n − k , 0 ) = C ( n − k + 1 , 0 ) C(n-k,0)=C(n-k+1,0) C(n−k,0)=C(n−k+1,0)
所以原式可以化简为 n − k + C ( n + 1 , k ) n-k+C(n+1,k) n−k+C(n+1,k)
由于输入的组数较多,所以这道题需要预处理。直接预处理阶乘是不现实的(因为 n n n和 m m m为 1 e 9 1e9 1e9,而且模数是会变化的)。因为模数 p p p( p < 10000 p<10000 p<10000)比较小,所以使用卢卡斯定理,就只需要处理10000以内的组合数
代码:
#include <cstdio>
#include <cmath>
using namespace std;
int fact[10005][1505];
int inv[10005][1505];
int mod;
int Prime[10005];
bool Isprime[10005];
int id[10005];
int pid;
int cnt=0;
void get_prime(){
for(int i=2;i<=10000;i++) Isprime[i]=1;
for(int i=2;i<=10000;i++){
if(Isprime[i]) id[i]=cnt,Prime[cnt++]=i;
for(int j=0;j<cnt&&Prime[j]*i<=10000;j++){
Isprime[Prime[j]*i]=0;
if(i%Prime[j]==0) break;
}
}
}
int quickpow(int base,int power,int mod){
int ret=1;
while(power){
if(power&1) ret=ret*base%mod;
base=base*base%mod;
power>>=1;
}
return ret;
}
int _inv(int x,int p){return quickpow(x,p-2,p)%p;}
void _pre(){
get_prime();
for(int i=0;i<cnt;i++) fact[0][i]=1,inv[0][i]=1;
for(int i=0;i<cnt;i++)
for(int j=1;j<Prime[i];j++){
fact[j][i]=fact[j-1][i]*j%Prime[i];
inv[j][i]=_inv(fact[j][i],Prime[i]);
}
}
int C(int n,int m){
if(m>n) return 0;
return fact[n][pid]*inv[m][pid]%mod*inv[n-m][pid]%mod;
}
int Lucas(int n,int m){
if(m==0) return 1;
return Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
int main(){
_pre();
int n,k;
int Case=1;
while(~scanf("%d%d%d",&n,&k,&mod)){
pid=id[mod];
if(2*k>n) k=n-k;
printf("Case #%d: %d\n",Case++,(n-k+Lucas(n+1,k))%mod);
}
return 0;
}
4.Mysterious For HDU - 4373
题意:
有两种循环:
for(int i=0;i<n;i++) //第一种
for(int i=0;i<n;i++) //第一种
for(int j=i;j<n;j++) //第二种
现已知共有 m m m个循环进行嵌套,其中 k k k个循环是第一种,每个循环的上限为 n n n,最内层循环中有一个函数 f ( x ) f(x) f(x),求该函数被调用了几次
分析:
一个第一种循环后跟了 t t t个第二种循环时,很容易通过找规律发现循环次数为 C ( n + t − 1 , t ) C(n+t-1,t) C(n+t−1,t)
由于模数为 364875103 = 97 ∗ 3761599 364875103=97*3761599 364875103=97∗3761599,并非质数
代码:
#include <cstdio>
#include <cmath>
using namespace std;
int pos[105];
const long long mod1=97;
const long long mod2=3761599;
const long long Mod=mod1*mod2;
long long fact1[105];
long long fact2[3000005];
long long e1,e2;
long long quickpow(long long base,long long power,long long mod){
long long ret=1;
while(power){
if(power&1) ret=ret*base%mod;
base=base*base%mod;
power>>=1;
}
return ret;
}
long long exgcd(long long a,long long b,long long &x,long long &y){
if(a%b==0){x=0;y=1;return b;}
long long d=exgcd(b,a%b,x,y);
long long temp=x;
x=y;y=temp-a/b*y;
return d;
}
long long inv(long long m,long long mod){ //求m在模mod下的逆元
long long x,y;
long long d=exgcd(m,mod,x,y);
if(d!=1) return -1; //若不互质则无逆元
long long temp=mod/d;
return (x%temp+temp)%temp; //返回最小正整数解
}
long long C(long long n,long long m,long long mod){
if(m>n) return 0;
if(mod==97) return fact1[n]*inv(fact1[m]*fact1[n-m]%mod,mod)%mod;
return fact2[n]*inv(fact2[m]*fact2[n-m],mod)%mod;
}
long long Lucas(long long n,long long m,long long mod){
if(!m) return 1;
return Lucas(n/mod,m/mod,mod)*C(n%mod,m%mod,mod)%mod;
}
void _pre(){
fact1[0]=1,fact2[0]=1;
for(int i=1;i<=96;i++) fact1[i]=fact1[i-1]*i%mod1;
for(int i=1;i<=3000000;i++) fact2[i]=fact2[i-1]*i%mod2;
e1=quickpow(mod2,mod1-2,mod1)*mod2;
e2=quickpow(mod1,mod2-2,mod2)*mod1;
}
int main(){
_pre();
int T;
scanf("%d",&T);
for(int Case=1;Case<=T;Case++){
int n,m;
scanf("%d%d",&n,&m);
int k;
scanf("%d",&k);
for(int i=0;i<k;i++) scanf("%d",&pos[i]);
pos[k++]=m;
long long ans=1;
for(int i=1;i<k;i++){
long long temp=pos[i]-pos[i-1];
long long a=Lucas(n+temp-1,temp,mod1)*e1%Mod;
long long b=Lucas(n+temp-1,temp,mod2)*e2%Mod;
ans*=(a+b);
ans%=Mod;
}
printf("Case #%d: %lld\n",Case,ans);
}
return 0;
}