大数计算

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Tan_tan_tann/article/details/88365244

                                                  大数计算

                                            时间限制: 1 Sec  内存限制: 512 MB

题目描述

输入

输出

            对于每个需要输出的操作,输出一行。

样例输入

          

样例1输入:
4 4
1 1 1 1
2 2 4
5 1 3
1 1 2
3 1 2


样例2输入:
10 10
2 3 4 2 6 1 7 3 5 4
4 3 7
2 4 5
1 3 3
1 5 2
4 2 8
3 3 6
1 8 2
5 7 9
4 1 10
3 2 6

 样例输出

样例1输出:
105
8
8



样例2输出:
484306675
86806489
798697982
432
80236263
799481293
1728

提示

 本题是一道简单的数论题,因为数据较大,所以我们需采取树状数组进行优化,将区间乘积记录下来,以加快运行速度。本题的各个要求操作的函数都是积极函数,方便修改。我们可以先建立是个记录数组,分别对应2、3、4、5号操作。由于精度问题,我们将所有除法操作全部换成乘上乘法逆元,以避免精度问题。

先用一个数组记录每个质数的次方的大小。

0号数组用于记录2号操作,每次更新时乘上prime[k]^t就行了。

1号数组用于记录3号操作,每次更新时乘上(a[k]+t+1)/(a[k]+1)

约数个数公式

T=p[1]^e[1]*p[2]^e[2]*...*p[n]^e[n]的约数个数为(e[1]+1)(e[2]+1)...(e[n]+1)

2号数组用于记录4号操作,每次乘上((prime[k]^(a[k]+t+1)-1))/(prime[k]^(a[k]+1)-1)

约数和公式

T=p[1]^e[1]*p[2]^e[2]*...*p[n]^e[n]的约数和为(1+p[1]^1+...+p[1]^e[1])...(1+p[n]^1...+p[n]^e[n])

3号数组用于记录4号操作,每次更新时乘上prime[k]^t就行了

本题的所有变量都必须开int,若开long long很容易时间超限,所以运算过程中必须十分小心,笔者就因为这个原因错了许多次,改了半天才改对

源码

#include<cstdio> 
#include<cmath> 
#include<cstring> 
#include<iostream> 
#include<algorithm> 
#define MAXN 1300000
using namespace std; 
typedef long long LL; 
const int mo=1000000007;
int n,zs[100005],c[100005][4],aa[100005];
int m;
inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s>'9'||s<'0'){if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}//读入优化
void write(int x)
{
    if(x<0)
    {
        x=(~x)+1;
        putchar('-');
    }
    if(x>9)
        write(x/10);
    putchar((x%10)^48);
}//输出优化
int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1;y=0;
        return a;
    }
    int r=exgcd(b,a%b,y,x);
    y-=(int)(((LL)a/(LL)b)*(LL)x);
    return r;
}//扩展欧几里得
inline int inv(int a,int p)
{
    int d,x,y;
    d=exgcd(a,p,x,y);
    if(d==1)
    {
        if(x%p<=0)
            return (x+p)%p;
        return x%p;
    }
    return -1;
}//求乘法逆元
inline int lowbit(int i)
{
    return i&-i;
}//不用解释,一个lowbit,树状数组用
inline void updata(int k,int x,int id)
{
    while(k<=n)
    {
        c[k][id]=(int)(((LL)c[k][id]*(LL)x)%mo);
        k+=lowbit(k);
    }
}//updata,进行每次的更新
int qkpow(int a,int s)
{
    int t=1;
    while(s>0)
    {
        if(s&1)
            t=(int)(((LL)t*(LL)a)%mo);
        a=(int)(((LL)a*(LL)a)%mo);
        s/=2;
    }
    return t%mo;
}//快速幂
inline int sum(int x,int id)
{
    int ans=1;
    while(x>0)
    {
        ans=(int)(((LL)ans*(LL)c[x][id])%mo);
        x-=lowbit(x);
    }
    return ans;
}//求区间值
int res(int t,int sd)
{
    return (int)(((LL)(qkpow(sd,t+1)-1)*(LL)inv(sd-1,mo))%mo);
}//求约数和
int main() 
{ 
    read(n);read(m);
    int len=0;bool vis[MAXN]={};
    for(int i=2;i<=MAXN&&len<n;i++)
        if(!vis[i])
        {
            len++;zs[len]=i;
            c[len][0]=c[len][1]=c[len][2]=c[len][3]=1;
            for(int j=i<<1;j<=MAXN;j+=i)
                vis[j]=true;
        }//欧拉筛,求质数
    for(int i=1;i<=n;i++)
    {
        read(aa[i]);
        updata(i,qkpow(zs[i],aa[i]),0);
        updata(i,1+aa[i],1);
        updata(i,res(aa[i],zs[i]),2);
        updata(i,(int)(((LL)qkpow(zs[i],aa[i]-1)*(LL)(zs[i]-1))%mo),3);
    }//初次输入,计算初值
    for(int i=1;i<=m;i++)
    {
        int id;read(id);
        if(id==1)
        {
            int k,t;read(k);read(t);
            aa[k]+=t;//更新指数大小
            updata(k,qkpow(zs[k],t),0);
            updata(k,inv(1+aa[k]-t,mo),1);
            updata(k,1+aa[k],1);
            updata(k,inv(res(aa[k]-t,zs[k]),mo),2);
            updata(k,res(aa[k],zs[k]),2);
            updata(k,qkpow(zs[k],t),3);
			//一号操作,进行更新
        }
        else
        {
            int l,r;read(l);read(r);
            write((int)(((LL)sum(r,id-2)*(LL)inv(sum(l-1,id-2),mo))%mo));//输出对应操作值
            putchar('\n');
        }
    }
    return 0; 
}

谢谢观赏!!!

猜你喜欢

转载自blog.csdn.net/Tan_tan_tann/article/details/88365244