1221 矩阵中不重复的元素V3

这是一个关于矩阵中不重复元素计数的数学问题。给定一个m*n的矩阵,其中每一列的元素是a的b次方到a+n-1的b次方,求矩阵中不重复的数的数量,考虑模10^9+7的结果。解决方案涉及到数论的因数分解和容斥原理,通过计算不同因数对应的数对组合并排除重复项来得到答案。
摘要由CSDN通过智能技术生成

一个m*n的矩阵。

该矩阵的第一列是a^b,(a+1)^b,.....(a + n - 1)^b

第二列是a^(b+1),(a+1)^(b+1),.....(a + n - 1)^(b+1)

.......

第m列是a^(b + m - 1),(a+1)^(b + m - 1),.....(a + n - 1)^(b + m - 1)

(a^b表示a的b次方)

下面是一个4*4的矩阵:

2^2=4, 2^3=8, 2^4=16, 2^5=32

3^2=9, 3^3=27, 3^4=81, 3^5=243

4^2=16, 4^3=64, 4^4=256, 4^5=1024

5^2=25, 5^3=125, 5^4=625, 5^5=3125

问这个矩阵里有多少不重复的数(比如4^3 = 8^2,这样的话就有重复了)

2^2=4, 2^3=8, 2^4=16, 2^5=32

3^2=9, 3^3=27, 3^4=81, 3^5=243

4^2=16, 4^3=64, 4^4=256, 4^5=1024

m = 4, n = 3, a = 2, b = 2。其中2^4与4^2是重复的元素。

由于数据会很大,输出Mod 1000000007(10^9 + 7)的结果。

 收起

输入

输入数据包括4个数:m,n,a,b。m,n为矩阵的长和宽(2 <= m,n <= 5 * 10^15)。a,b为矩阵的第1个元素,a^b(2 <= a , b <= 10^15)。

输出

输出不重复元素的数量Mod 1000000007(10^9 + 7)的结果。

输入样例

4 3 2 2

输出样例

11

题解:

先考虑 xa=ybxa=yb 意味着什么

记 p(x)p(x) 为最小的 pp, 使得 ∃k,pk=x∃k,pk=x。 那么 xa=p(x)k(x)×axa=p(x)k(x)×a

则 xa=ybxa=yb 等价于 p(x)k(x)×a=p(y)k(y)×bp(x)k(x)×a=p(y)k(y)×b 等价于 p(x)=p(y)∧k(x)×a=k(y)×bp(x)=p(y)∧k(x)×a=k(y)×b

考虑枚举 pp ,计算所有 p(x)=pp(x)=p 的 xx 带来的重复

那么 使 xx 在给定区间 的 k(x)k(x) 在一个区间中

于是问题转化为,求 card{i×j|i∈[l1,r1],j∈[l2,r2]}card{i×j|i∈[l1,r1],j∈[l2,r2]}其中l1l1,r1r1为O(log2(n))O(log2(n))级别,l2l2,r2r2为 O(n)O(n) 级别

容斥。

先加上在 [l1,r1][l1,r1] 中任选一个数,并在 [l2,r2][l2,r2] 任选一个数的方案数

再减去在 [l1,r1][l1,r1] 任选两个数 x,yx,y,并在 [l2,r2][l2,r2] 任选两个数 a,ba,b ,使得x×a=y×bx×a=y×b的方案数

再加上在 [l1,r1][l1,r1] 任选三个数 x,y,zx,y,z,并在 [l2,r2][l2,r2] 任选三个数 a,b,ca,b,c ,使得x×a=y×b=z×cx×a=y×b=z×c的方案数 ......

可以发现 当 [l1,r1][l1,r1] 中的数确定之后, [l2,r2][l2,r2] 的方案数是可以算出来的 因为乘积一定为 lcmlcm 的倍数

设为 xx 倍 则l2≤x×lcm/max&&x×lcm/min≤r2l2≤x×lcm/max&&x×lcm/min≤r2 于是 l2×max≤x×lcm≤r2×minl2×max≤x×lcm≤r2×min

所以 xx 的取值数=r2×min/lcm−l2×max/lcm+1r2×min/lcm−l2×max/lcm+1

于是暴力容斥复杂度 =O(2r1−l1+1)=O(n)=O(2r1−l1+1)=O(n)

l1l1,r1r1一共有 log22(n)log22(n)种

对于所有的一样的l1l1,我们可以从小到大枚举r1r1计算。 总复杂度还是 O(n)O(n)

当然还过不了

注意到 如果一个数选了之后对于lcm,max,minlcm,max,min没有影响 那么他选和不选的贡献就会抵消

所以先枚举max,minmax,min 之后dfsdfs 如果碰到对lcmlcm无贡献的数,就直接退出

同时,如果选了它导致前面的数成为对lcmlcm无贡献的数,那么它就不选

这样优化之后复杂度就很好了 李陶冶说"比较接近于光滑数的数量(其实比这个还要低一个数量级)"

但我不知道光滑数是什么东西 总之O(跑的过)

还有个问题,就是如何求出有多少个p(x)p(x)会使得k(x)k(x)在 [l1,r1][l1,r1] 中

我是筛出n−−√n以内所有p(x)=xp(x)=x的xx 然后枚举 求更优的方法。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int D=1e9+7,Lo=52;
int cnt[Lo+2][Lo+2];
ll L1,R1,l2,r2,ans,now;
void init()
{
    ll m,n,a,b;
    cin>>m>>n>>a>>b;
    ans=(m%D)*(n%D)%D;
    L1=a;R1=a+n-1;l2=b;r2=b+m-1;
}

int Gcd[Lo+2][Lo+2];
int gcd(int x,int y)
{
    while(y)swap(x%=y,y);
    return x;
}

int l1,r1,mn;
ll L,R;//L(=l2*mx)<=x*Lcm<=R(=r2*mn)   
int mx_p[Lo+5],st[Lo+5],*top;
#define len (R/Lcm - (L+Lcm-1)/Lcm +1)
void dfs(int *h,const ll &Lcm,bool ji)
{
    if(Lcm>R)return ;
    if(h>top)
    {
        if(ji)now-=len;
        else now+=len;
        return ;
    }
    int x=*h;
    if(Lcm%x==0) return ;
    //if(len<=0) return ;
    dfs(h+1,Lcm,ji);
    dfs(h+1,Lcm*(x/Gcd[x][Lcm%x]),ji^1);
}

const int U=8e7;
bool mark[U];

int main()
{
    //freopen("1.in","r",stdin);freopen("1.out","w",stdout);
    init();
    int mp=sqrt(R1);
    for(int p=2;p<=mp;++p)
    if(!mark[p])
    {
        int k=0;
        ll x=1;
        while(x<L1&&x<=R1/p) { x*=p;if(x<=mp)mark[x]=1;++k; }
        if(x<L1||x>R1)continue;
        int l1=k; 
        while(x<=R1/p) { x*=p;if(x<=mp)mark[x]=1;++k; }
        if(k>l1)
        ++cnt[l1][k];
    }

    for(int i=1;i<=Lo;++i)
    for(int j=0;j<i;++j)
     Gcd[i][j]=gcd(i,j);
    for(int i=1;i<=Lo;++i)
    for(int j=1;j<i;++j)
    if(i%j==0) mx_p[i]=j; 

    for(l1=1;l1<=Lo;++l1)
    {
        int mr=0;
        for(r1=Lo;r1>l1;--r1)
        if(cnt[l1][r1]){mr=r1;break;}

        now=0;
        for(r1=l1+1;r1<=mr;++r1)
        {        
            L=l2*r1;
            for(mn=l1;mn<r1;++mn)
            {
               R=r2*mn;    
               if(L>R)continue;

               int Lcm=r1*(mn/gcd(r1,mn));
               int i=mn+1;
               for(;i<r1;++i)
               if(Lcm%i==0) break;
               if(i<r1)continue;

               top=st-1;
               for(int i=mn+1;i<r1;++i)
               if(mx_p[i]<=mn) 
                *++top=i;

               dfs(st,Lcm,0); 
            }
            now%=D;
            ans-=cnt[l1][r1]*now%D;
        }
    }
    cout<<(ans%D+D)%D;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值