csu 2014 summer training day 2 莫比乌斯反演

SPOJ VLATTICE

题意:x,y,z<=1000000,x<=a,y<=b,z<=c,给定a、b、c,求gcd(x,y,z)=1的个数

解释:设 f(n)是gcd(x,y,z)=n的种数,F(n)=n|gcd(x,y,z)的种数

那么F(n)=f(n)+f(2n)....=sigm(f(d)){n|d}

那么根据反演公式 f(n)=sigm(u(d/n)*F(d)){n|d}

我们要求的是f(1)=sigm(u(1)*F(n)+u(2)*F(2n)+u(3)*F(3n).....) kn<=min(a,b,c)

F(d)=(x/d)*(y/d)*(z/d)

依次求和即可,求u(x)部分是模板

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 1001000
 6 #define LL long long
 7 using  namespace std;
 8
 9 int K[maxn];
10 int C[maxn];
11 int U[maxn];
12 bool flag[maxn];
13 int prim[maxn/3];
14 int cnt;
15 void culc(){
16     cnt=0;
17     U[1]=1;
18     memset(flag,0,sizeof(flag));
19     for(int i=2;i<=1000000;i++){
20         if (!flag[i]){
21             prim[cnt++]=i;
22             U[i]=-1;
23         }
24         for(int j=0;j<cnt;j++){
25             if (i*prim[j]>1000000) break;
26             flag[i*prim[j]]=true;
27             if (i % prim[j]==0){
28                 U[i*prim[j]]=0;
29                 break;
30             }else {
31                 U[i*prim[j]]=-U[i];
32             }
33         }
34     }
35     return ;
36 }
37 LL solve(int N){
38     LL ans=0;
39     for(int i=1;i<=N;i++){
40         LL k=N/i;
41         ans=ans+U[i]*k*k*(k+3);
42     }
43
44     return ans;
45 }
46 int t;
47 int N;
48 int main(){
49     culc();
50     scanf("%d",&t);
51     while(t--){
52         scanf("%d",&N);
53         LL ans=solve(N);
54         printf("%lld\n",ans+3);
55     }
56     return 0;
57 }

HDU 1695

题意:

给定b,d,K,求x<=b,y<=d中gcd(x,y)=K的个数,其中gcd(3,5)和gcd(5,3)算一种

分析:和上题类似,关键部分:

if(b > d)swap(b,d);
    LL ans1=0,ans2=0;
    for(int i=1; i<=b;i++)
        ans1+=(LL)U[i]*(b/i)*(d/i);
    for(int i=1; i<=b;i++)
        ans2+=(LL)U[i]*(b/i)*(b/i);
    ans1-=ans2/2;简要说一下,当i>b时每次都加0,没意义。当x<=b,y>b的时候,(b,d)只计算了一次当x<=b,y<=b的时候,(b,d),(d,b)重复出现了两次所以ans=ans1-ans2/2;

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 100100
 6 #define LL long long
 7 using  namespace std;
 8
 9 int U[maxn];
10 bool flag[maxn];
11 int prim[maxn/3];
12 int cnt;
13 void culc(){
14     cnt=0;
15     U[1]=1;
16     memset(flag,0,sizeof(flag));
17     for(int i=2;i<=100000;i++){
18         if (!flag[i]){
19             prim[cnt++]=i;
20             U[i]=-1;
21         }
22         for(int j=0;j<cnt;j++){
23             if (i*prim[j]>100000) break;
24             flag[i*prim[j]]=true;
25             if (i % prim[j]==0){
26                 U[i*prim[j]]=0;
27                 break;
28             }else {
29                 U[i*prim[j]]=-U[i];
30             }
31         }
32     }
33     return ;
34 }
35 LL solve(int b,int d){
36 //    LL ans=0;
37 //    for(int i=1;i<=max(N,M);i++){
38 //        int m1=min(N/i,M/i);
39 //        int m2=max(N/i,M/i)-m1;
40 //        ans=ans+(LL)U[i]*(m1*(m1-1)/2+m1+m1*m2);
41 //    }
42     if(b > d)swap(b,d);
43     LL ans1=0,ans2=0;
44     for(int i=1; i<=b;i++)
45         ans1+=(LL)U[i]*(b/i)*(d/i);
46     for(int i=1; i<=b;i++)
47         ans2+=(LL)U[i]*(b/i)*(b/i);
48     ans1-=ans2/2;
49     return ans1;
50 }
51 int t;
52 int N,M,K;
53 int main(){
54     culc();
55     scanf("%d",&t);
56     for(int cas=1;cas<=t;cas++){
57         scanf("%d%d%d%d%d",&N,&N,&M,&M,&K);
58         LL ans;
59         if (K==0) ans=0;
60         else ans=solve(N/K,M/K);
61         printf("Case %d: %I64d\n",cas,ans);
62     }
63     return 0;
64 }

HYSBZ 2818

题意:给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的
数对(x,y)有多少对.时限很长

分析:这里要求的gcd是素数,我们只要枚举N以内的素数即可,再求出相应的gcd=prim即可

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 10010000
 6 #define LL long long
 7 using  namespace std;
 8
 9 int U[maxn];
10 bool flag[maxn];
11 int prim[maxn/3];
12 int cnt;
13 void culc(){
14     cnt=0;
15     U[1]=1;
16     memset(flag,0,sizeof(flag));
17     for(int i=2;i<=10000000;i++){
18         if (!flag[i]){
19             prim[cnt++]=i;
20             U[i]=-1;
21         }
22         for(int j=0;j<cnt;j++){
23             if (i*prim[j]>10000000) break;
24             flag[i*prim[j]]=true;
25             if (i % prim[j]==0){
26                 U[i*prim[j]]=0;
27                 break;
28             }else {
29                 U[i*prim[j]]=-U[i];
30             }
31         }
32     }
33     return ;
34 }
35 LL solve(int N){
36     LL ans=0;
37     for(int i=1;i<=N;i++){
38         ans=ans+(LL)U[i]*(N/i)*(N/i);
39     }
40     return ans;
41 }
42 int t;
43 int N,M,K;
44 int main(){
45     culc();
46     scanf("%d",&N);
47     LL ans=0;
48     for(int i=0;i<cnt;i++) {
49         if (prim[i]>N) break;
50         ans+=solve(N/prim[i]);
51     }
52     printf("%lld\n",ans);
53     return 0;
54 }

HYSBZ 2005

题意:一共有n列,每列有m棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标(x, y)来表示,其中x的范围是1至n,表示是在第x列,y的范围是1至m,表示是在第x列的第y棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了一个角上,坐标正好是(0, 0)。 能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器连接而成的线段上有k棵植物,则能量的损失为2k + 1。例如,当能量汇集机器收集坐标为(2, 4)的植物时,由于连接线段上存在一棵植物(1, 2),会产生3的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植物,则能量损失为1。现在要计算总的能量损失。

分析:很好的一道题,代码中优详细说明

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 100000
 6 #define LL long long
 7 using  namespace std;
 8
 9 LL F[maxn+100];
10 int main(){
11     LL n,m;
12     while(~scanf("%lld%lld",&n,&m)){
13         if (n>m) swap(n,m);
14         LL ans=0;
15         for(int i=n;i>=1;i--){//我们要保证每次能除掉2i,3i...即比i大的F(x),所以i只能从大向小求取
16             F[i]=(n/i)*(m/i);//gcd(x,y)=i,2i,3i....的数量
17             for(int j=2*i;j<=n;j+=i){//枚举
18                 F[i]-=F[j];//去除掉gcd(x,y)=2i,3i,4i....的部分
19             }
20             //F[i]=gcd(x,y)=i的数量
21             ans+=F[i]*(2*i-1);//既然是取得的第i项,那么这些
22         }
23         printf("%lld\n",ans);
24     }
25     return 0;
26 }

HYSBZ 2301(综合)

题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

分析:化简:gcd等式两边同除以k,求gcd=1即可,集合间的关系,再用分段优化

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 50000
 6 #define LL long long
 7 using  namespace std;
 8
 9 int U[maxn+100];
10 bool flag[maxn+100];
11 int prim[maxn/3+100];
12 int cnt;
13 void culc(){
14     cnt=0;
15     U[1]=1;
16     memset(flag,0,sizeof(flag));
17     for(int i=2;i<=maxn;i++){
18         if (!flag[i]){
19             prim[cnt++]=i;
20             U[i]=-1;
21         }
22         for(int j=0;j<cnt;j++){
23             if (i*prim[j]>maxn) break;
24             flag[i*prim[j]]=true;
25             if (i % prim[j]==0){
26                 U[i*prim[j]]=0;
27                 break;
28             }else {
29                 U[i*prim[j]]=-U[i];
30             }
31         }
32     }
33     return ;
34 }
35 int sum[maxn+100];
36 //找[1,n],[1,m]内互质的数的对数
37 LL solve(int n,int m){
38     LL ans=0;
39     if (n>m) swap(n,m);
40     for(int i=1,last=0;i<=n;i=last+1){
41         last=min(n/(n/i),m/(m/i));
42         ans+=(LL)(sum[last]-sum[i-1])*(n/i)*(m/i);
43     }
44     return ans;
45 }
46
47 int t;
48 int a,b,c,d,k;
49 int main(){
50     culc();
51     sum[0]=0;
52     for(int i=1;i<=maxn;i++){
53         sum[i]=sum[i-1]+U[i];
54     }
55     scanf("%d",&t);
56     for(int cas=1;cas<=t;cas++){
57         scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
58         LL ans;
59         a--,c--;
60         ans=solve(b/k,d/k)-solve(a/k,d/k)-solve(b/k,c/k)+solve(a/k,c/k);
61         printf("%lld\n",ans);
62     }
63     return 0;
64 }

ZOJ 3435

题意:求gcd(x,y,z)=1的个数,但是卡时限,要分段优化,注意代码

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <iostream>
 5 #define maxn 1000000
 6 #define LL long long
 7 using  namespace std;
 8
 9 int U[maxn+100];
10 bool flag[maxn+100];
11 int prim[maxn/3+100];
12 LL sum[maxn+100];
13 int cnt;
14 void culc(){
15     cnt=0;
16     U[1]=1;
17     memset(flag,0,sizeof(flag));
18     for(int i=2;i<=maxn;i++){
19         if (!flag[i]){
20             prim[cnt++]=i;
21             U[i]=-1;
22         }
23         for(int j=0;j<cnt;j++){
24             if (i*prim[j]>maxn) break;
25             flag[i*prim[j]]=true;
26             if (i % prim[j]==0){
27                 U[i*prim[j]]=0;
28                 break;
29             }else {
30                 U[i*prim[j]]=-U[i];
31             }
32         }
33     }
34     sum[0]=0;
35     for(int i=1;i<=maxn;i++) sum[i]=sum[i-1]+U[i];
36     return ;
37 }
38 LL solve(int a,int b,int c){
39     int m=max(a,b);m=max(m,c);
40     LL ans=0;
41     int Inf=1000100;
42     for(int i=1,last;i<=m;i=last+1){
43         last=Inf;
44         if (i<=a) last=min(last,a/(a/i));
45         if (i<=b) last=min(last,b/(b/i));
46         if (i<=c) last=min(last,c/(c/i));
47 //        cout<<"last="<<last;
48         ans+=(sum[last]-sum[i-1])*(((LL)a/last+1)*((LL)b/last+1)*((LL)c/last+1)-1);
49 //        cout<<"ans="<<ans<<endl;
50     }
51     return ans;
52 }
53 int a,b,c;
54 int main(){
55     culc();
56     while(scanf("%d%d%d",&a,&b,&c)!=EOF){
57         a--;b--;c--;
58         LL ans=solve(a,b,c);
59         printf("%lld\n",ans);
60     }
61     return 0;
62 }

csu 2014 summer training day 2 莫比乌斯反演

时间: 2024-10-08 07:51:32

csu 2014 summer training day 2 莫比乌斯反演的相关文章

csu 2014 summer training day 3 简单树形dp

FZU 2157 题意:树上的节点可以打上0或1的标记,树的权值由两部分呢组成,点权和边权,有00.01.10.11四种组合的边权, 问最小权值和.以1节点为树根 分析:dp[x][0]表示x标记0后的最小的权值,dp[x][1]同理 那么每次可以计算dp[x][0],dp[x][1]: 例如dp[x][1]=min(dp[son][0]+lab[0][1]+val[1],dp[son][1]+bal[1][1]+val[1]); 具体看代码. 用bfs写的分层,深搜代码更简洁 1 #inclu

HDU-4947-GCD Array(树状数组+莫比乌斯反演)

Problem Description Teacher Mai finds that many problems about arithmetic function can be reduced to the following problem: Maintain an array a with index from 1 to l. There are two kinds of operations: 1. Add v to ax for every x that gcd(x,n)=d. 2.

bzoj 2820 / SPOJ PGCD 莫比乌斯反演

那啥bzoj2818也是一样的,突然想起来好像拿来当周赛的练习题过,用欧拉函数写掉的. 求$(i,j)=prime$对数 \begin{eqnarray*}\sum_{i=1}^{n}\sum_{j=1}^{m}[(i,j)=p]&=&\sum_{p=2}^{min(n,m)}\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}[i⊥j]\newline&=&\sum_{p=

hdu1695(莫比乌斯反演)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1695 题意: 对于 a, b, c, d, k . 有 x 属于 [a, b],  y 属于 [c, d], 求 gcd(x, y) = k 的 x, y 的对数 . 其中 a = b = 1 . 注意: (x, y), (y, x) 算一种情况 . 思路: 莫比乌斯反演 可以参考一下: http://blog.csdn.net/lixuepeng_001/article/details/5057

算法学习——莫比乌斯反演(1)

.. 省选GG了,我果然还是太菜了.. 突然想讲莫比乌斯反演了 那就讲吧! 首先我们看一个等式-- (d|n表示d是n的约束) 然后呢,转换一下 于是,我们就发现! 没错!F的系数是有规律的! 规律is here! 公式: 这个有什么卵用呢? 假如说有一道题 F(n)可以很simple的求出来而求f(n)就比较difficult了,该怎么办呢? 然后就可以用上面的式子了 是莫比乌斯函数,十分有趣 定义如下: 若d=1,则=1 若d=p1*p2*p3...*pk,且pi为互异素数,则=(-1)^k

bzoj2301 [HAOI2011]Problem b【莫比乌斯反演 分块】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2301 很好的一道题.首先把每个询问转化为4个子询问,最后的结果就是这四个子询问的记过加加减减,类似二维前缀和.那么问题转化为在1 <= x <= lmtx, 1 <= y <= lmty时gcd(x, y) == k的对数,这个问题在转化一下,转化成1 <= x <= lmtx / k,1 <= y <= lmty / k时x与y互质的对数.莫比乌斯反

BZOJ2301: [HAOI2011]Problem b 莫比乌斯反演

分析:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. 然后对于求这样单个的gcd(x,y)=k的,我们通常采用莫比乌斯反演 但是,时间复杂度是O(n*(n/k))的,当复杂度很坏的时候,当k=1时,退化到O(n^2),超时 然后进行分块优化,时间复杂度是O(n*sqrt(n)) #include<cstdio> #include<cstring> #include<queue

BZOJ2005: [Noi2010]能量采集 莫比乌斯反演的另一种方法——nlogn筛

分析:http://www.cnblogs.com/huhuuu/archive/2011/11/25/2263803.html 注:从这个题收获了两点 1,第一象限(x,y)到(0,0)的线段上整点的个数是gcd(x,y) 2,新学了一发求gcd(x,y)=k有多少对的姿势,已知0<x<=n,0<y<=m 令x=min(n,m),令f[i]代表gcd(x,y)=i的对数, 那么通过O(xlogx)的复杂度就可以得到f[1]到f[n](反着循环) 普通的容斥(即莫比乌斯反演)其实也

容斥原理与莫比乌斯反演的关系

//容斥原理,c[i]表示i当前要算的次数,复杂度和第二层循环相关 O(nlogn~n^2) LL in_exclusion(int n,int *c) { for(int i=0;i<=n;i++) c[i]=1; //不一定是这样初始化,要算到的才初始化为1 LL ans=0; for(int i=0;i<=n;i++) if(i要算) { ans+=(统计数)*c[i]; for(int j=i+1;j<=n;j++) if(i会算到j) c[j]-=c[i];//j要算的次数减去