TrickGCD

来源:互联网 发布:淘宝嘉定仓库 编辑:程序博客网 时间:2024/05/20 08:22

TrickGCD

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)

Total Submission(s): 746    Accepted Submission(s): 293



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 kth 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
144 4 4 4
 

Sample Output
Case #1: 17
 

PS:证明尚不是很完善,想明白继续填坑,各种细节持续补充中…

题意:

       给你一个数组A,求一个数组B,数组B中的元素满足两个条件。

       1. 1<=bi<=ai

       2. 数组B中任意bi~br,其gcd>=2

       求所有符合上述条件的B数组的种类数。


思路:

       我们先找出数组A中最小的数amin,然后从1遍历到amin,我们用a[j]/i找出A中所有元素含有多少个这个因子,然后将A中个元素所得种类数相乘,再把从1到amin中的所有种类数相加。最后因为有重复因子出现,所以要考虑去重的情况。下面用例子仔细说明这个思路。

       1

       4

       4 5 6 8

       amin=4,我们就从2遍历到4。

       i=2时,4里面有2个2,代表取种类的时候可以取2,4,(4/2=2),5里面有22,代表取种类的时候可以取2,4,(5/2=2)6里面有32,代表取种类的时候可以取246(6/2=3)8里面有42,代表取种类的时候可以取2468(8/2=4).然后所有i=2时我们得出的结果就应该是2*2*3*4=48,即4可以取2种,5可以取2种…所以总种数是相乘。然后显然,我们应该把i=3i=4的情况加起来。但是我们看i=4的情况,会出现4444这一种情况,而在i=2时,也会出现4444这一种情况,所以这就出现一种重复,我们就需要去重。这一题我们可以借助莫比乌斯函数来实现如何去重,下面介绍为什么要使用莫比乌斯。


为什么使用莫比乌斯函数?

       我们把i从2遍历到amin,所有的i共有4种情况。

       1. i本身是一个质数

       2. i可以分解成奇数个不同的质数相乘

       3. i可以分解成偶数个不同的质数相乘

       4. i可以分解成有相同质数相乘的形式

       我们解题的总体思路是讨论i的所有因子,表示在遍历i的这些因子时,我们都考虑过i,对遍历的结果都贡献了“1”,但我们考虑的最后结果是,必须要保证所有的值都只考虑了一次,所以我们就要讨论对重复考虑的情况进行去重。比如i=6,在i=2,i=3,i=6的时候都考虑过6的这种情况,这就会产生重复,6重复考虑了3次,下面介绍如何解决这种重复。以i的四种情况分别讨论去重的情况。我们记a[i]为遍历到i时,考虑去重后所得的系数。

       先说结论再证明:

       1. a[i]=1  2. a[i]=1  3. a[i]=1  4. a[i]=0

       证明如下:

       1. i本身是一个质数,所以他的因子(从2开始)只有他自己,从2遍历,i只出现了一次,所以a[i]=1.

       2. 若i可分解成奇数个不同的质数相乘。例如30,可分解成2*3*5=30。30的因子为2 3 5 6 10 15 30。其中235都只出现了一次,次数分别+1,到6时,我们发现6=2*310=2*515=3*5,这三种情况都为分解成偶数个不同质数相乘的情况(具体参考情况3),所以61015-1,到30时,30在前面所有的因子遍历中出现了0次,所以应该+1才能保证出现了30出现了一次。将这个情况做一个推广。若i可分解成n个不同的质数相乘,从2一直到i的所有因子,其个数应该为C(n,1)+C(n,2)++C(n,n-1)+C(n,n),其中C(n,1)为所有的质因子个数,C(n,2)应为所有两两质因子的组合所得出的因子个数,以此类推。所以根据奇数个质因子相乘+1,偶数个质因子相乘-1的情况,可以得出C(n,1)-C(n,2)+C(n,3)-C(n,4)+-C(n,n-1)(n为奇数)+a[i]的系数=1,由于C(n,m)=C(n,n-m)可得C(n,1)-C(n,2)+C(n,3)-C(n,4)+-C(n,n-1)(n为奇数)等于0,故a[i]=1.

       3. 证明过程类似于2.只不过n为偶数,式子可写成C(n,1)-C(n,2)+C(n,3)-C(n,4)+…+C(n,n-1)(n为偶数)+a[i]=1,由计算可得C(n,1)-C(n,2)+C(n,3)-C(n,4)+…+C(n,n-1)(n为偶数)=2*(C(n,1)-C(n,2)+…(-1)^n/2 * C(n,n/2-1))+(-1)^n/2+1 * C(n,n/2),再由组合数的性质,C(n,k)=C(n-1,k-1)+C(n-1,k)可得,此式为2,所以a[i]的系数为-1.

       4. 我们将这种情况分为两种情况介绍。

          <1>. i是平方数。(例如4,16,25)首先介绍一个定义,一个数m,若m的因子中含有平方数(4,9,16…),我们就称m为含有平方因子的数。介绍一个结论,从n到n²中,所有n²的因子都是含有平方因子的数,所以按照含有平方因子的系数都为0的情况,我们只需要考虑从2到n就可以。而此时n可以再当作i做判断,再循环到1.2.3.4任意一种情况再做处理,处理之后的n,一定是只出现过一次的情况,(即此时,从2到n,实现了n²只出现了一次),而根据n到n²的所有a的值都为0,故若保证最后结果为1,必使a[n²]=0。(从n到n²的每一个含有平方因子的数,都可以再次进去4进行循环,可得a[m]=0)。

          <2>. i不是平方数。(例如12,18,32)我们可以把它分解成有相同质数相乘的形式,例如12可以分解成2*2*3.由于出现重复的因子,我们可以先把他当作一个因子来看待,找出其与i的因子的区别。例如12,若我们根据2,3来找出12的因子,应该为2,3,6.而实际12的因子还有4,12.对比我们发现少的因子全部为含有平方数的因子。这个结论并不特殊。因为我们删除重复因子后,就相当于删除了两个(或多个)重复因子相乘的因子,例如删除了2后,我们也就删除了2*2=4这个因子,删除了2*2*3=12这个因子,也就是说,我们删除了所有能分解成两个或多个相同因子相乘的i的因子,而根据4,这些因子的结果都为0。(这些因子再进到4里面进行循环,最后得出结果为0)所以我们可以不再考虑这些因子,再将剩下的因子再次判断1.2.3.4进行循环,根据递推逐次推导,得出最后i的结果。(相当于从大到小遍历i的一部分因子)。如a[12]=0.

       由此,此题结论全部证明完毕。下面介绍莫比乌斯函数。

       u(n)=1,n=1

       u(n)=(-1)^k,n=p1*p2*…pk (p为不同的素因子)

       u(n)=0,其余情况

       我们分析莫比乌斯函数,发现恰好u[i]=-a[i],(其证明过程在某些方面确与莫比乌斯函数相似)。所以此题可以用莫比乌斯函数实现去重的过程。


公式:

       根据上述过程,我们可以得出此题的公式为:[i2amin遍历求和][-u(i)*(j1n遍历求积)(a[j]/i)]

       然后我们对这个公式进行优化,我们把a[j]/i相同的放在一起,求遍历i时每一区域内的因子数,然后用快速幂降低时间复杂度,优化后的式子为:[i2amin遍历求和][-u(i)*(j1j*i<=100000)(j^num[j*i+i-1]-num[j*i-1])


下面贴上代码:

#include<cstdio>#include<cstring>#include<iostream>using namespace std;#define ll long longconst int maxn=200000;const int mod=1e9+7;ll a;ll mu[maxn+10];ll num[maxn+10];bool check[maxn+10];ll prime[maxn+10];void mobius(){    memset(check,0,sizeof(check));    mu[1]=1;    ll tot=0;    for(ll i=2; i<maxn; i++)    {        if(!check[i]) //check[i]代表i是否是素数,check[i]=1代表i不是素数        {            prime[tot++]=i; //prime是素数集 prime[0]=2            mu[i]=-1; //mu[i]代表莫比乌斯函数u[i]        }        for(int j=0; j<tot; j++)        {            if(i*prime[j]>maxn)                break;            check[i*prime[j]]=true;            if(i%prime[j]==0)            {                mu[i*prime[j]]=0;                break;            }            else                mu[i*prime[j]]=-mu[i];        }    }}ll quickpow(ll x,ll y) //ans=x^y{    ll ans=1;    while(y)    {        if(y%2==1)            ans=ans*x%mod;        x=(x*x)%mod;        y=y>>1;    }    ans=ans%mod;    return ans;}int main(){    mobius();    int T;    scanf("%d",&T);    int cas=1;    while (T--)    {        memset(num,0,sizeof(num));        int n;        scanf("%d",&n);        ll amin=maxn;        for(int i=1; i<=n; i++)        {            scanf("%lld",&a);            amin=min(amin,a); //amin存储A数组中的最小值            num[a]++;        }        for(int i=1; i<maxn; i++)            num[i]+=num[i-1]; //num[i]表示数组A中小于等于i的数的个数        ll res=0;        for(ll i=2; i<=amin; i++)        {            ll temp=1;            for(ll j=1; j*i<=100000LL; j++)                temp=(temp*quickpow(j,num[j*i+i-1]-num[j*i-1])%mod)%mod;            res=(res-temp*mu[i]%mod+mod)%mod;        }        printf("Case #%d: %lld\n",cas++,res);    }    return 0;}

原创粉丝点击