Codeforces Round #705 (Div. 2)
D. GCD of an Array
题意:给出一个长度为n的数组,每次对其中一个元素进行乘法操作,然后询问整个数组的GCD。
首先肯定想到的是,对数组的元素进行质因数分解,然后每次更新只用更新单个元素的质因数表达式。同时维护所有元素共有质因数的最小次幂,以便求GCD。
之后需要考虑的式用什么数据结构来维护每个元素的质因数表达式和所有元素共有质因数的最小次幂。
1、对于维护每个元素的质因数表达式。如果用vector则不便于修改,方法之一是可以使用map建立质因数和其次幂之间的映射,每次修改和查询所需的时间复杂度为
O
(
l
o
g
n
)
O(logn)
O(logn)。
2、对于维护所有元素共有质因数的最小次幂,一开始的想法是用一个数组来维护,但是数组只能维护历史最小值,并不能动态维护最小值。解决方法之一是使用multiset,用每个multiset维护一个质因数的所有次幂,当且仅当集合大小为n时,表明这个质因数是所有元素共有质因数,因为multiset自动维护集合中元素的有序性,所以集合第一个元素即为最小次幂,修改即是对集合的插入和删除,复杂度为
O
(
l
o
g
n
)
O(logn)
O(logn)。
3、每次修改后都重新遍历所有集合求GCD的做法时间复杂度太大,大概是
O
(
q
l
o
g
n
+
q
n
)
O(qlogn+qn)
O(qlogn+qn),注意到当且仅当集合的大小为n时,该质因数对答案才有贡献,而且修改只会让更多的质因数作出更多的贡献,所以我们可以维护
a
n
s
ans
ans,每次修改时,对
a
n
s
ans
ans造成变化只可能是修改的那个元素,所以我们只需要记录修改前质因数的最小次幂和修改后质因数的最小次幂,两者做差
x
x
x,那么该质因数
p
p
p对答案的贡献为
p
x
p^x
px。这样一来询问的时间复杂度大致为
O
(
q
l
o
g
n
)
O(qlogn)
O(qlogn)。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int max_n=2e5+5;
const int mod=1e9+7;
int prime[max_n];
int vis[max_n];
map<int,int> f[max_n];
multiset<int> e[max_n];
multiset<int>::iterator it;
set<int> vi;
int cnt=0;
int tot,q;
ll ans=1;
void init(void)
{
for(int i=2;i<max_n;i++)
{
if(!vis[i])prime[cnt++]=i;
for(int j=0;j<cnt&&i*prime[j]<max_n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
ll qpow(ll a,int n,int mod)
{
ll ans=1;
while(n)
{
if(n&1)ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
void update(int x,int v,map<int,int> &f)
{
int pre=0;
if(e[x].size()==tot)pre=*(e[x].begin());
it=e[x].find(f[x]);
if(it!=e[x].end())e[x].erase(it);
f[x]+=v;
e[x].insert(f[x]);
if(e[x].size()==tot)ans=ans*qpow((ll)x,*(e[x].begin())-pre,mod)%mod;
}
void resolve(int n,map<int,int> &f)
{
for(int i=0;i<cnt&&prime[i]*prime[i]<=n;i++)
if(n%prime[i]==0){
int time=0;
int pre=0;
while(n%prime[i]==0)n/=prime[i],time++;
update(prime[i],time,f);
if(n==1)break;
}
if(n!=1){
update(n,1,f);
}
}
int main(void)
{
init();
scanf("%d%d",&tot,&q);
int x,index;
for(int i=1;i<=tot;i++)
{
scanf("%d",&x);
resolve(x,f[i]);
}
while(q--)
{
scanf("%d%d",&index,&x);
resolve(x,f[index]);
printf("%lld\n",ans);
}
}
//2 2
//2 2
//1 2
//2 2