题目链接:https://codeforces.com/contest/1493/problem/D
题目大意:输入两个数,
接下来输入个数
,
接下来次询问。
每次询问输入,我们需要把
乘以
,
,求出
,答案模以
。
题解:由于数字可能很大,我们需要将数字进行素因子分解,可以用存数字素因子分解的形式。
对于素因子分解,我们可以采用埃拉托色尼筛选法
对于每个素数,我们可以用或
维护素数的幂出现的次数。
由于每次都是乘操作,答案只增不降,对于每次询问,我们可以维护当前的答案,每次只更新变化的素因子的值。
复杂度:,
为素因子个数。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int nn =210000;
const int inff = 0x3fffffff;
const double eps = 1e-8;
typedef long long LL;
const double pi = acos(-1.0);
const LL mod = 1000000007;
int n,q;
int a[nn];
bool isprime[nn];
vector<int> primeV;
vector<pair<int,int>> numPrime[nn];
map<int,int> aPrime[nn];
map<int,int> currentPrime[nn];
LL ans;
void calc(int v)
{
int i=v;
for(int j=0;j<int(primeV.size());j++)
{
int jnum = 0;
while(v%primeV[j]==0)
{
v/=primeV[j];
jnum++;
}
if(jnum > 0)
{
numPrime[i].push_back(make_pair(primeV[j],jnum));
}
if(v<=1)
break;
}
}
LL POW(LL x,LL y)
{
LL ret=1;
while(y)
{
if(y&1)
{
ret=(ret*x)%mod;
}
x=(x*x)%mod;
y>>=1;
}
return ret;
}
void updatePrime(int prime,int beforev,int nowv)
{
int beforefirst=currentPrime[prime].begin()->first;
currentPrime[prime][beforev]--;
if(currentPrime[prime][beforev]==0)
{
currentPrime[prime].erase(beforev);
}
currentPrime[prime][nowv]++;
int nowfirst = currentPrime[prime].begin()->first;
if(beforefirst!=nowfirst)
{
ans=(ans*POW(prime,nowfirst-beforefirst))%mod;
}
}
void add(int i,int v)
{
for(auto it=numPrime[v].begin();it!=numPrime[v].end();it++)
{
int beforev = aPrime[i][it->first];
aPrime[i][it->first]+=it->second;
updatePrime(it->first,beforev,aPrime[i][it->first]);
}
}
int main()
{
memset(isprime,true,sizeof(isprime));
isprime[0]=isprime[1]=false;
for(int i=2;i<=200000;i++)
{
if(isprime[i])
{
primeV.push_back(i);
for(int j=i+i;j<=200000;j+=i)
{
isprime[j]=false;
}
}
}
for(int i=2;i<=200000;i++)
{
calc(i);
}
cin>>n>>q;
for(int i=0;i<int(primeV.size());i++)
{
currentPrime[primeV[i]][0]=n;
}
ans=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
add(i,a[i]);
}
while(q--)
{
int i,x;
scanf("%d%d",&i,&x);
add(i,x);
printf("%lld\n",ans);
}
return 0;
}
本题还可以用线段树来解决,我们用线段树维护区间的最大公约数,用来存最大公约数的素因子表达式。
对于每次更新,我们只需要更新中出现的素数。
时间复杂度:,
为素因子个数。
线段树解决代码中的素因子分解方法更好:
首先求出每个数字的最小素因子:
memset(nextPrime,0,sizeof(nextPrime));
nextPrime[0]=nextPrime[1]=1;
for(int i=2;i<=200000;i++)
{
if(nextPrime[i]!=0)
continue;
nextPrime[i]=i;
if(i>1000)
continue;
for(int j=i*i;j<=200000;j+=i)
{
if(nextPrime[j]==0)
nextPrime[j]=i;
}
}
再利用循环求出数字的素因子:
void SplitePrime(int val)
{
int id=val;
while(val>1)
{
int num=0;
int cur=nextPrime[val];
while(cur==nextPrime[val])
{
val/=nextPrime[val];
num++;
}
splitePrime[id].push_back(make_pair(cur,num));
}
}
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int nn =210000;
const int inff = 0x3fffffff;
const double eps = 1e-8;
typedef long long LL;
const double pi = acos(-1.0);
const LL mod = 1000000007;
int n,q;
int a[nn];
int treel[nn*20],treer[nn*20];
vector<pair<int,int>> splitePrime[nn];
int nextPrime[nn];
unordered_map<int,int> treev[nn*20];
int treeG[nn*20];
LL ans;
int GCD(int x,int y)
{
if(y==0)
return x;
return GCD(y,x%y);
}
LL POW(LL x,LL y)
{
LL ret=1;
while(y)
{
if(y&1)
{
ret=(ret*x)%mod;
}
x=(x*x)%mod;
y>>=1;
}
return ret;
}
void SplitePrime(int val)
{
int id=val;
while(val>1)
{
int num=0;
int cur=nextPrime[val];
while(cur==nextPrime[val])
{
val/=nextPrime[val];
num++;
}
splitePrime[id].push_back(make_pair(cur,num));
}
}
void add(int i,int val)
{
for(auto it=splitePrime[val].begin();it!=splitePrime[val].end();it++)
{
int before = treev[i][it->first];
treev[i][it->first]+=it->second;
if(i==1 && before!=treev[i][it->first])
ans=(ans*POW(it->first,treev[i][it->first]-before))%mod;
}
treeG[i]=val;
}
void pushup(int i)
{
treeG[i]=GCD(treeG[2*i],treeG[2*i+1]);
for(auto it=splitePrime[treeG[i]].begin();it!=splitePrime[treeG[i]].end();it++)
{
treev[i][it->first]=it->second;
if(i==1)
ans=(ans*POW(it->first,it->second))%mod;
}
}
void build(int i,int l,int r)
{
treel[i]=l;
treer[i]=r;
if(l==r)
{
add(i,a[l]);
return;
}
int mid=(l+r)/2;
build(2*i,l,mid);
build(2*i+1,mid+1,r);
pushup(i);
}
void update(int i,int id,int x)
{
if(treel[i]==id&&treer[i]==id)
{
add(i,x);
return;
}
int mid=(treel[i]+treer[i])/2;
if(id<=mid)
update(2*i,id,x);
else
update(2*i+1,id,x);
for(auto it=splitePrime[x].begin();it!=splitePrime[x].end();it++)
{
int v = it->first;
if(treev[2*i].find(v)!=treev[2*i].end() && treev[2*i+1].find(v)!=treev[2*i+1].end())
{
int before = treev[i][v];
treev[i][v]=min(treev[2*i+1][v],treev[2*i][v]);
if(i==1&& before!=treev[i][v])
ans=(ans*POW(v,treev[i][v]-before))%mod;
}
}
}
int main()
{
memset(nextPrime,0,sizeof(nextPrime));
nextPrime[0]=nextPrime[1]=1;
for(int i=2;i<=200000;i++)
{
if(nextPrime[i]!=0)
continue;
nextPrime[i]=i;
if(i>1000)
continue;
for(int j=i*i;j<=200000;j+=i)
{
if(nextPrime[j]==0)
nextPrime[j]=i;
}
}
for(int i=2;i<=200000;i++)
{
SplitePrime(i);
}
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ans=1;
build(1,1,n);
while(q--)
{
int i,x;
scanf("%d%d",&i,&x);
update(1,i,x);
printf("%lld\n",ans);
}
return 0;
}