昨天晚上用的镜像,看的B的图片瞬间不想写了(而且这周作业还没碰),不过看到D题突然想做做,于是有了下面的思路,写了一个小时,写完没交看了下榜单发现C题竟然过的人也不多,看了看C题感觉没啥思路就跑去补作业了~~
D. GCD of an Array
由于题目中要求gcd\gcdgcd取模,显然gcd(x%mod,y%mod)≠gcd(x,y)%mod\gcd(x\%mod,y\%mod)\ne\gcd(x,y)\%modgcd(x%mod,y%mod)=gcd(x,y)%mod,于是很容易想到维护数组每个数质因数分解后的幂次x=p1α1p2α2…pkαkx=p_1^{\alpha_1}p_2^{\alpha_2}\dots p_k^{\alpha_k}x=p1α1p2α2…pkαk,而pkp_kpk对最终gcd\gcdgcd的贡献是pkmin(α1,k,α2,k,…,αn,k)p_k^{\min(\alpha_{1,k},\alpha_{2,k},\dots,\alpha_{n,k})}pkmin(α1,k,α2,k,…,αn,k)
而对某个位置i×i×i×一个数x=p1αi,1p2αi,2…pkαi,kx=p_1^{\alpha_{i,1}}p_2^{\alpha_{i,2}}\dots p_{k}^{\alpha_{i,k}}x=p1αi,1p2αi,2…pkαi,k则表示修改αi,1,αi,2,…,αi,k\alpha_{i,1},\alpha_{i,2},\dots,\alpha_{i,k}αi,1,αi,2,…,αi,k这些值(准确来说是+上一个数,我们只需要记录之前的值,把之前的贡献减去,然后把现在的贡献加上即可)
而对于贡献的维护我们需要一个能够支持插入,删除,最小值的数据结构,这里使用multiset\text{multiset}multiset
trick:对于初始数组可以看出一个操作i,aii,a_ii,ai
时间复杂度:O{(n+m)max(ai)logn}O\{(n+m)\sqrt{\max(a_i)}\log{n}\}O{(n+m)max(ai)logn}
#define IO ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long;
constexpr ll mod=1e9+7;
constexpr int N=200010;
int a[N],n,m;
ll d;
multiset<int> s[N];
map<int,int> mp[N];
ll qmi(ll a,ll b)
{ll res=1;while(b){if(b&1) res=res*a%mod;b>>=1;a=a*a%mod;}return res;
}
ll inv(ll x)
{return qmi(x,mod-2);
}
void insert(int k,int i,int cnt)
{if(mp[k].count(i)){if(s[i].size()==n) d=d*inv(qmi(i,*s[i].begin()))%mod;s[i].erase(s[i].find(mp[k][i]));mp[k][i]=mp[k][i]+cnt;s[i].insert(mp[k][i]);if(s[i].size()==n) d=d*qmi(i,*s[i].begin())%mod;}else{mp[k][i]=cnt;s[i].insert(mp[k][i]);if(s[i].size()==n) d=d*qmi(i,*s[i].begin())%mod;}
}
void divide(int k,int x)
{for(int i=2;i<=x/i;i++)if(x%i==0){int cnt=0;while(x%i==0) x/=i,cnt++;insert(k,i,cnt);}if(x>1) insert(k,x,1);
}
int main()
{IO;cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];d=1;for(int i=1;i<=n;i++) divide(i,a[i]);while(m--){int i,x;cin>>i>>x;divide(i,x);cout<<d<<'\n';}return 0;
}