2018 Multi-University Training Contest 7
1001:Age of Moyu
1002:
1005:GuGuFishtion
首先推公式,欧拉函数是积性函数但不是一个完全积性函数,所以a,b不互质的情况下肯定还有一个偏移量。然后质因数分解一下a,b可以用数论知识证明差一个GCD(a,b)/Φ(GCD(a,b)),所以对于所有a,b不互质的情况下对答案的贡献是GCD(a,b)/Φ(GCD(a,b)),互质的话贡献是1。然后容斥,枚举k,容斥计算以k作为gcd的数对有多少个,然后乘上贡献就行了。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=1000000+5; LL tmp,ans; long long n,m,p; long long phi[N], cnt, prime[N/3],dy[N], val[N], q[N],sum_val[N],sum[N],inv[N],mu[N]; bool com[N]; int primes; void get_prime_phi(){ memset( com, false, sizeof( com ) ); primes = 0; phi[1] = 1; mu[1] = 1; for (int i = 2; i < N; ++i){ if (!com[i]) { prime[primes++] = i; phi[i] = i-1; mu[i] = -1; } for (int j = 0; j < primes && i*prime[j] <= N; ++j){ com[i*prime[j]] = true; if (i % prime[j]){ phi[i*prime[j]] = phi[i]*(prime[j]-1); mu[i*prime[j]] = -mu[i]; } else { mu[i*prime[j]] = 0; phi[i*prime[j]] = phi[i]*prime[j]; break; } } } } int lim; long long f[N],F[N]; void get(){ for(int i=1;i<=lim;++i) F[i]=1ll*(m/i)*(n/i)%p; for(int i=lim;i>0;--i){ f[i]=F[i]; for(int j=i+i;j<=lim;j+=i) f[i]-=1ll*f[j]%p; } for(int i=1;i<=lim;++i) f[i]%=p; } int main(){ //freopen("case.in","r",stdin); int T; scanf("%d",&T); get_prime_phi(); while(T--){ scanf("%lld%lld%lld",&n,&m,&p); tmp=ans=0; lim=min(n,m); memset(inv,0,sizeof(inv)); memset(f,0,sizeof(f)); inv[1]=1; for(register int i=2;i<=lim+2;i++) inv[i]=(LL)(p-p/i)*inv[p%i]%p; get(); for(register int i=1;i<=lim;i++){ val[i]=(LL)i*inv[phi[i]]%p; ans=(ans+(LL)val[i]*f[i]%p)%p; if(ans<0) ans+=p; //cout<<ans<<endl; } printf("%lld\n",ans); } return 0; }
View Code
1006:
有意思的dp;
根据异或的性质,X_0⊕X_1⊕X_2⊕X_3⊕…..⊕X_(M-1)=S⊕T
计算出S⊕T中一共有cnt个1,将这个二进制数标准化,使它成为前面N-cnt个0,后cnt个1的二进制数,答案保持不变,问题变为:用K个N位且有3个1的不同二进制数进行异或,最终得到前面N-cnt个0,后面cnt个1这个二进制数,有多少种方案?
递推。设d[K][M]表示用K个N位且有3个1的不同二进制数进行异或,最终得到前面N-cnt个0,后面cnt个1这个二进制数的方案数。
然后可以容易推出状态只可以从上一层的-1,-3,+1,+3这些状态转移过来
考虑加入重复串的影响,d[i][j]-= (d[i-2][j] * (C[n][3]-i+2);表示去除加入i-2个数字并且后面有j个1这个二进制数但后来又异或上两个之前没用过的相同的数的方案数,
考虑二进制数加入的先后顺序给答案带来的额外贡献,d[i][j]=d[i][j]*inv[i]%mod
#include <cstdio> #include <cstdlib> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <string> #include <map> using namespace std; const long long mod=19260817; long long dp[30][50],c[50][50],inv[110]; char S[45],T[45]; int n,k; void init() { memset(dp,0,sizeof(dp)); } int main() { int i,j; c[0][0]=1; for(i=1;i<=45;i++) { c[i][0]=c[i][i]=1; for(j=1;j<i;j++) { c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } } inv[1]=1; for(i=2;i<=60;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; int cas=1; while(scanf("%d%d",&n,&k)!=EOF) { if(n==0&&k==0) break; init(); scanf("%s%s",S,T); int num_1=0; for(i=0;i<n;i++) if((S[i]-'0')^(T[i]-'0')==1) num_1++; dp[0][0]=1; for(i=1;i<=k;i++) { for(j=0;j<=n;j++) { if(j>=3) dp[i][j]=(dp[i][j]+dp[i-1][j-3]*c[j][3])%mod; if(j>=1) dp[i][j]=(dp[i][j]+dp[i-1][j-1]*c[j][2]%mod*c[n-j][1])%mod; if(j+3<=n) dp[i][j]=(dp[i][j]+dp[i-1][j+3]*c[n-j][3])%mod; if(j+1<=n) dp[i][j]=(dp[i][j] + dp[i - 1][j + 1] * c[j][1] %mod* c[n - j][2]) % mod; if (i - 2 >= 0)dp[i][j] = (dp[i][j] - (dp[i - 2][j] * (c[n][3] - i + 2))) % mod; dp[i][j] = dp[i][j] * inv[i] % mod; if(dp[i][j]<0) dp[i][j]+=mod; } } printf("Case #%d: %lld\n",cas++, dp[k][num_1]); } return 0; }
View Code
1007:
1008:
1009:
1010:
矩阵快速幂,首先可以看出这题只能用矩阵快速幂,然后考虑对p进行分块,处理出p/i这个矩阵的指数,然后就矩阵快速幂了
1011: