2017 多校 TrickGCD

本文通过一道算法题目详细解析了莫比乌斯反演的原理及其在容斥原理中的应用,展示了如何从已知的G(x)反推求得F(x),并通过具体实例解释了如何利用该技巧高效解决特定的数学问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

TrickGCD

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 671    Accepted Submission(s): 257


Problem Description
You are given an array  A  , and Zhu wants to know there are how many different array  B  satisfy the following conditions?

1BiAi
* For each pair( l , r ) ( 1lrn ) ,  gcd(bl,bl+1...br)2
 

Input
The first line is an integer T( 1T10 ) describe the number of test cases.

Each test case begins with an integer number n describe the size of array  A .

Then a line contains  n  numbers describe each element of  A

You can assume that  1n,Ai105
 

Output
For the  k th test case , first output "Case #k: " , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer  mod   109+7
 

Sample Input
  
1 4 4 4 4 4
 

Sample Output
  
Case #1: 17
 

Source
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   6055  6054  6053  6052  6051 
 

先说说这个题我学到了啥吧。 

莫比乌斯反演的定义是正常我们求函数是G(X)=kF(X)  是知道F(X)求G(X)  然后这个反演就是知道G(X)求F(X) 反过来求
然后对于容斥原理  反演公式:   F(n)=sigma(U(n/d)*G(d)) sigma是求和  这里U是一个函数,他是每一项 G(d) 的系数 U[x] = (-1)^r
 如果 ei中(1<=i<=r)有一个数ei大于1  那么  U[x] = 0;    对于一个x这个数来说必定能分解因式为x=(p1^e1)*(p2^e2)...*(pr^er) 比
如2=2^1  有一个因子  6=2^1*3^1 有2个因子 比如12=2^2*3^1 那么U[6]=1 U[12]=0 U[2]=-1 然后
在计算 F(6)的时候,我们会用到 G(1) G(2) G(3) G(6)


我们考察者4个G


G(1) = F(1)


G(2) = F(1)+F(2)


G(3) = F(1)+F(3)


G(6) = F(1)+F(2)+F(3)+F(6)


当我们需要逆向有G得到F(n)时,只需要将一些 与 F(n) 有关的 G进行容斥!!!!! 最终组合得到F(n)!!!


比如 F(6) = G(6)-G(2)-G(3)+G(1) 

这就得到了6所有的情况 所以 窝们就能得到ai时候所有的情况 就不会被容斥卡了


正解:先枚举gcd,然后每个位置有a[i]/d种方案,如果枚举1-n已经o(n)了,再求a[i]位置还有一个质因子的复杂度。 只能把a[i]/d相同的放在一起算,当然会有重复的情况,还得容斥一下  具体做法如下:


//china no.1
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <vector>
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <cstring>
#include <queue>
#include <list>
#include <stdio.h>
#include <set>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <cctype>
#include <sstream>
#include <functional>
#include <stdlib.h>
#include <time.h>
#include <bitset>
using namespace std;

#define pi acos(-1)
#define endl '\n'
#define srand() srand(time(0));
#define me(x,y) memset(x,y,sizeof(x));
#define foreach(it,a) for(__typeof((a).begin()) it=(a).begin();it!=(a).end();it++)
#define close() ios::sync_with_stdio(0); cin.tie(0);
typedef long long LL;
const int INF=0x3f3f3f3f;
const LL LINF=0x3f3f3f3f3f3f3f3fLL;
const int dx[]={-1,0,1,0,1,-1,-1,1};
const int dy[]={0,1,0,-1,-1,1,-1,1};
const int maxn=1e3+1e2;
const int maxx=1e5+1e2;
const double EPS=1e-7;
const LL mod=1e9+7;
template<class T>inline T min(T a,T b,T c) { return min(min(a,b),c);}
template<class T>inline T max(T a,T b,T c) { return max(max(a,b),c);}
template<class T>inline T min(T a,T b,T c,T d) { return min(min(a,b),min(c,d));}
template<class T>inline T max(T a,T b,T c,T d) { return max(max(a,b),max(c,d));}
#define FOR(x,n,i) for(int i=x;i<=n;i++)
#define FOr(x,n,i) for(int i=x;i<n;i++)
#define W while
#define sgn(x) ((x) < 0 ? -1 : (x) > 0)
#define bug printf("***********\n");

inline int Scan()
{
    int Res=0,ch,Flag=0;
    if((ch=getchar())=='-')Flag=1;
    else if(ch>='0' && ch<='9')Res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')Res=Res*10+ch-'0';
    return Flag ? -Res : Res;
}
LL sum[maxx],p[maxx];
//p[x]  为gcd为x的个数  
//第i个位置有a[i]/d种选择
//若a[i]/d=k贡献为k,则和它相同贡献有y=sum[kd,(k+1)d-1]个。
//累乘下来也就是k^y
//最后容斥减掉gcd为jx的部分(j>1)
int a[maxx];
int _pow(LL a,LL n)
{
    LL ret=1;
    while(n)
    {
        if(n&1)  ret=ret*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ret;
}
LL sb(LL x)
{
    while(x<=0)
        x+=mod;
}
int main()
{
    int t,n;
   // freopen( "in.txt" , "r" , stdin );
    t=Scan();
    for(int cas=1;cas<=t;cas++)
    {
        n=Scan();
        me(sum,0);
        me(p,0);
        int maxc=0;
        for(int i=1;i<=n;i++)
        {
            a[i]=Scan();
            sum[a[i]]++;
            maxc=max(maxc,a[i]);
        }
        for(int i=1;i<=maxc;i++)
            sum[i]+=sum[i-1];
        for(int i=maxc;i>=2;i--)
        {
            p[i]=1;
            if(sum[i-1])//如果a[i]里有比当前d还小的数那么gcd为d的一定不存在
            {
                p[i]=0;//所以计算这一个d没有意义了
                //例如 2 3 4 8 sum[i] 为0 1 2 3 3 3 3 4 下一位sum[7] 还有
                //他的下一位还有  计算以8为gcd已经没有意义了
                continue;
            }
            for(int j=i;j<=maxc;j+=i)
            {
                LL num=sum[min(maxc,i+j-1)]-sum[j-1];
                LL x=j/i;
                if(num)
                    p[i]=p[i]*_pow(x,num)%mod;
            }
        }
        LL ans=0;
        for(int i=maxc;i>=2;i--)
        {
            for(int j=i+i;j<=maxc;j+=i)
                p[i]-=(p[j]-mod)%mod;
            ans+=p[i];
            ans%=mod;
        }
        ans%=mod;
        printf("Case #%d: %lld\n",cas,ans);
    }
    //cerr << "run time is " << clock() << endl;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值