7.25 鸽巢原理,中国剩余定理,欧拉函数
Posted raincle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了7.25 鸽巢原理,中国剩余定理,欧拉函数相关的知识,希望对你有一定的参考价值。
A题:给你一个序列,长度为n。问是否存在一个连续的子序列和是m的倍数
鸽巢原理,求出序列的前缀和数组,若pre[i]%m==pre[j]%m,则(pre[j]-pre[i])%m==0;
B题:给出5个整数a,b,c,d,k。你要在[a,b]中找一个x,在[c,d]中找一个y,使得gcd(x,y)=k。请输出所有可能的组合数。
注意(x=5, y=7) 和 (x=7, y=5)是一样的。所有数据a=c=1;
gcd(x,y)=k,则gcd(x/k,y/k)=1,问题转化为求有多少组互素的x,y;
我们将b/=k;d/=k;枚举从1到b每一个x,求在(1,d)区间有多少与x互素的数;用容斥原理来求,与x互素的数的个数等于(d-与x不互素的元素的个数)=(d-与x有相同因子的元素的个数);
故将x质因数分解,套用容斥模板即可;
这里注意,因为(5,7)(7,5)是一样的,所以我们枚举1到min(b,d)的x,将x 质因数分解,然后求区间[x,d]与x互素的元素的个数(为什么这里是[x,d]不是[x+1,d]是因为1与1互素,如果要写[x+1,d]最后让ans++即可;)(为什么是[x,d]而不是[1,d],是因为避免重复,例如x=2,你找到了3,5,7;那么在找3,5,7的时候,你又把2算了一遍,所以我们总是从大于等于x的区间找;
这里特别注意区间和问题,求[l,r]=solve(r)-solve(l-1);(不要把 l 这个点给剪掉了)
1 #include <iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 #define LL long long 7 const int maxn=1e6; 8 int cnt,p=0; 9 bool isprime[maxn]; 10 int prime[maxn]; 11 int prime_n[maxn]; 12 void init() 13 { 14 LL i,j; 15 for(i=2;i<maxn;i++) 16 { 17 if(!isprime[i]) 18 { 19 prime[p++]=i; 20 for(j=i*i;j<maxn;j+=i) 21 isprime[j]=true; 22 } 23 } 24 } 25 void only(LL n) 26 { 27 LL i,j; 28 cnt=0; 29 for(i=0;i<p&&prime[i]*prime[i]<=n;i++) 30 { 31 if(n%prime[i]==0) 32 { 33 prime_n[cnt++]=prime[i]; 34 while(n%prime[i]==0) 35 { 36 n/=prime[i]; 37 } 38 } 39 } 40 if(n>1)prime_n[cnt++]=n; 41 } 42 LL solve(LL r) 43 { 44 LL i,j,ans=0; 45 for(i=1;i<(1<<cnt);i++) 46 { 47 LL tnt=0,mult=1; 48 for(j=0;j<cnt;j++) 49 { 50 if((i>>j)&1) 51 { 52 mult*=prime_n[j]; 53 tnt++; 54 } 55 } 56 if(tnt&1)ans+=r/mult; 57 else ans-=r/mult; 58 } 59 return r-ans; 60 } 61 int main() 62 { 63 init(); 64 int t; 65 cin>>t; 66 int T=1; 67 while(t--) 68 { 69 int a,b,c,d,k; 70 scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); 71 if(k==0||k>b||k>d)printf("0 "); 72 else 73 { 74 b/=k; 75 d/=k; 76 int n=min(b,d); 77 int m=max(b,d); 78 LL ans=0; 79 int res,i; 80 for(i=1;i<=n;i++) 81 { 82 only(i); 83 ans+=solve(m)-solve(i-1); 84 } 85 printf("Case %d: %lld ",T,ans); 86 } 87 T++; 88 } 89 return 0; 90 }
C题:欧拉树
发现树的两边是完全对称的,只看左边,递归求解,最简分式上下互素,对于n,存在以n为分母的个数为n的欧拉数
D题:HHM非常喜欢读书,已知他每天看a1页,那么最后还有b1页没有看;看a2页,还有b2页没有看....看ak页还有bk页没有看。请问这本书有多少页
典型的中国剩余算法问题
即xmod a1=b1;
xmod a2=b2;
xmod a3=b3;
.......
因为题目没有说a1,a2,..an互素,所以用不互素的中国剩余定理模板即可。
求出来的数即为最小满足条件的x
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 typedef long long LL; 5 typedef pair<LL, LL> PLL; 6 LL a[100000], b[100000], m[100000]; 7 LL gcd(LL a, LL b){ 8 return b ? gcd(b, a%b) : a; 9 } 10 void ex_gcd(LL a, LL b, LL &x, LL &y, LL &d){ 11 if (!b) {d = a, x = 1, y = 0;} 12 else{ 13 ex_gcd(b, a % b, y, x, d); 14 y -= x * (a / b); 15 } 16 } 17 LL inv(LL t, LL p){//如果不存在,返回-1 18 LL d, x, y; 19 ex_gcd(t, p, x, y, d); 20 return d == 1 ? (x % p + p) % p : -1; 21 } 22 PLL linear(LL A[], LL B[], LL M[], int n) {//求解A[i]x = B[i] (mod M[i]),总共n个线性方程组 23 LL x = 0, m = 1; 24 for(int i = 0; i < n; i ++) { 25 LL a = A[i] * m, b = B[i] - A[i]*x, d = gcd(M[i], a); 26 if(b % d != 0) return PLL(0, -1);//答案,不存在,返回-1 27 LL t = b/d * inv(a/d, M[i]/d)%(M[i]/d); 28 x = x + m*t; 29 m *= M[i]/d; 30 } 31 x = (x % m + m ) % m; 32 return PLL(x, m);//返回的x就是答案,m是最后的lcm值 33 } 34 int main(){ 35 int n; 36 while(scanf("%d", &n) != EOF){ 37 for(int i = 0; i < n; i ++){ 38 a[i] = 1; 39 scanf("%d%d", &m[i], &b[i]); 40 } 41 PLL ans = linear(a, b, m, n); 42 if(ans.second == -1) printf("-1 "); 43 else printf("%I64d ", ans.first); 44 } 45 }
以上是关于7.25 鸽巢原理,中国剩余定理,欧拉函数的主要内容,如果未能解决你的问题,请参考以下文章