大数计算
时间限制: 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;
}