zoukankan      html  css  js  c++  java
  • 数论练习

    1, CF 870F Paths 

    大意: n节点图, 节点$x,y$之间有一条边当且仅当$gcd(x,y) ot= 1$.

    求$sumlimits_{u=1}^nsumlimits_{v=u+1}^n d(u,v)$, $d$为最短路数组, 不连通时为0.

    $1$和$>n/2$的素数显然是孤立点.

    考虑其他点对$(x,y)$, 假设$p_x,p_y$为$x,y$的最小素因子.

    若$gcd(x,y)>1$, 有$x ightarrow y, space d(x,y)=1$

    若$gcd(x,y)=1$且$p_xp_y<=n$, 有$x ightarrow p_xp_y ightarrow y, space d(x,y)=2$

    若$gcd(x,y)=1$且$p_xp_y>n$, 有$x ightarrow 2p_x ightarrow 2p_y ightarrow y, space d(x,y)=3$

    分别对这三类点计数即可.

    对于情况1, 直接欧拉函数求和.

    对于情况2, 感觉不是很好算, 但是可以欧拉函数算出情况2与情况3的和

    对于情况3, x,y中一定有一个是$>sqrt{n}$的素数, 我们枚举这个素数计算即可.

    复杂度$O(n)$.

    const int N = 1e7+10;
    int phi[N], p[N], s[N], a[N], cnt, vis[N];
    void init(int n) {
        phi[1] = 1;
        for (int i=2; i<=n; ++i) {
            if (!vis[i]) p[++cnt]=i,phi[i]=i-1, ++a[cnt];
            for (int j=1,t; j<=cnt&&i*p[j]<=n; ++j) {
                vis[t=i*p[j]] = 1, ++a[j];
                if (i%p[j]==0) {phi[t]=phi[i]*p[j];break;}
                phi[t]=phi[i]*phi[p[j]];
            }
        }
    	REP(i,1,cnt) s[i]=s[i-1]+a[i];
    }
    
    int main() {
    	int n;
    	scanf("%d", &n), init(n);
    	ll a1 = 0, a2 = 0, a3 = 0, sum = 0, tot = 0;
    	REP(i,2,n) a1 += i-1-phi[i], tot += phi[i]-1;
    	int now = cnt;
    	REP(i,1,cnt) if ((ll)p[i]*p[i]>n) {
    		while ((ll)p[i]*p[now]>n) --now;
    		if (p[i]<=n/2) a3 += s[i-1]-s[now];
    		sum += s[i-1]-s[now];
    	}
    	a2 = tot-sum;
    	printf("%lld
    ", a1+a2*2+a3*3);
    }

    练习2. CF 585E Present for Vitalik the Philatelist

    大意: n张邮票, 第$i$张价格$a_i$, Vitalik先买一张$a_x$, 店主在剩余邮票中选一个总$gcd$为$g$的邮票集合, 要求$g ot=1$且$gcd(a_x,g)=1$, 求所有合法方案数.

    经典计数题, 设$f(x)$为$x|g$的方案数, $g(x)$为$x=g$的方案数, $cnt_x$为$x|a_i$的$a_i$个数

    有$f(x)=(n-cnt_x)(2^{cnt_x}-1), f(x)=sumlimits_{x|d}g(d)$

    根据莫比乌斯反演有$g(x)=sumlimits_{x|d}mu(frac{d}{x})f(d)$

    答案即为$sumlimits_{x=2}^ng(x)=f(1)-g(1)=-g(1)$

    要注意这里$g(1)$是没有实际意义的, 只是为了反演计算.

    const int N = 1e7+10;
    int mu[N], p[N], cnt, vis[N];
    void init(int n) {
        mu[1] = 1;
        for (int i=2; i<=n; ++i) {
            if (!vis[i]) p[++cnt]=i,mu[i]=-1;
            for (int j=1,t; j<=cnt&&i*p[j]<=n; ++j) {
                vis[t=i*p[j]] = 1;
                if (i%p[j]==0) {mu[t]=0;break;}
                mu[t]=-mu[i];
            }
        }
    }
    
    int n, a[N];
    int main() {
    	scanf("%d", &n);
    	int ma = 0;
    	REP(i,1,n) {int t; scanf("%d", &t),++a[t],ma=max(ma,t);}
    	init(ma);
    	int ans = 0;
    	REP(i,1,ma) if (mu[i]) {
    		int c = 0;
    		for (int j=i; j<=ma; j+=i) c+=a[j];
    		(ans-=mu[i]*(n-c)*(qpow(2,c)-1)%P)%=P;
    	}
    	if (ans<0) ans+=P;
    	printf("%d
    ", ans);
    }
    

    练习3. CF 803F Coprime Subsequences

    大意: 给定序列, 求多少子序列gcd为1.

    水题, 按照练习2的套路可以直接秒, 相当于$f(x)=2^{cnt_x}-1$最后求$g(1)$

    这里再给出一种不需要求$mu$, 直接筛出$g$的方法

    因为$f(x)=sumlimits_{x|d}g(d)$, 就有$g(x)=f(x)-sumlimits_{x|d,x ot= d}g(d)$, 我们可以倒序枚举即可.

    const int N = 1e7+10;
    int n, a[N], g[N];
    
    int main() {
    	scanf("%d", &n);
    	int ma = 0;
    	REP(i,1,n) {int t; scanf("%d", &t),++a[t],ma=max(ma,t);}
    	PER(i,1,ma) {
    		int c = 0;
    		for (int j=i; j<=ma; j+=i) c+=a[j],(g[i]-=g[j])%=P;
    		(g[i]+=qpow(2,c)-1)%=P;
    	}
    	if (g[1]<0) g[1]+=P;
    	printf("%d
    ", g[1]);
    }
    

    练习4. CF 439E Devu and Birthday Celebration

    大意: 求和为n, 长度为k, gcd为1的序列个数

    又是一道水题, $f(x)$为$x|gcd$的方案数, 有$f(x)=inom{frac{n}{x}-1}{k-1}$

    那么反演一下可以得到$gcd=1$的方案数为$summu(d)f(d)=summu(d)inom{frac{n}{d}-1}{k-1}$

    const int N = 1e5+10;
    int mu[N], vis[N], p[N], cnt;
    ll fac[N], ifac[N];
    void init(int n) {
        mu[1] = 1;
        for (int i=2; i<=n; ++i) {
            if (!vis[i]) p[++cnt]=i,mu[i]=-1;
            for (int j=1,t; j<=cnt&&i*p[j]<=n; ++j) {
                vis[t=i*p[j]] = 1;
                if (i%p[j]==0) {mu[t]=0;break;}
                mu[t]=-mu[i];
            }
        }
    	fac[0] = ifac[0] = 1;
    	REP(i,1,n) fac[i]=fac[i-1]*i%P,ifac[i]=inv(fac[i]);
    }
    int C(int n, int m) {
    	if (n<m) return 0;
    	return fac[n]*ifac[m]%P*ifac[n-m]%P;
    }
    
    int main() {
    	init(N-1);
    	int q;
    	scanf("%d", &q);
    	while (q--) {
    		int n, f, ans = 0;
    		scanf("%d%d", &n, &f);
    		int mx = sqrt(n+0.5);
    		REP(i,1,mx) if (n%i==0) {
    			(ans+=mu[i]*C(n/i-1,f-1))%=P;
    			if (i!=n/i) (ans+=mu[n/i]*C(i-1,f-1))%=P;
    		}
    		if (ans<0) ans+=P;
    		printf("%d
    ", ans);
    	}
    }

    练习5 CF 665F Four Divisors

    大意: 求有多少个不超过n的数, 满足只有四个因子.

    比较水, 可以发现所有四因子数是两个素数的乘积或者一个素数的三次幂, 转化为求素数个数.

    可以用LOJ6235 或者hdu5901的方法

  • 相关阅读:
    托付和事件的使用
    在使用supervisord 管理tomcat时遇到的小问题
    无法安装vmware tools的解决方PLEASE WAIT! VMware Tools is currently being installed on your system. Dependin
    (转)Openlayers 2.X加载高德地图
    (转)openlayers实现在线编辑
    (转) Arcgis for js加载百度地图
    (转)Arcgis for js加载天地图
    (转) 基于Arcgis for Js的web GIS数据在线采集简介
    (转) Arcgis for js之WKT和GEOMETRY的相互转换
    (转)Arcgis for Js之Graphiclayer扩展详解
  • 原文地址:https://www.cnblogs.com/uid001/p/10629206.html
Copyright © 2011-2022 走看看