题解:
刚拿到这个题目的时候我就想,就线段树维护区间gcd,这不就成了吗,tree[rt].gcd = gcd(tree[lson].gcd, tree[rson].gcd) ,又是单点修改,连lazy标记都不用,然后我又仔细想了想发现不对劲,因为gcd对模没有和加减一样类似的性质,也就是说。。。我的以上思路不对。。。。(cf网站中一些大佬给出了AC了的线段树代码,暂时还没研究) 看了一下官方题解,以下是对官方题解的部分翻译以及自己的一些理解,(官方题解全英文)
主导思想是质因数分解!因为我们发现不管是最开始的n个数还是之后的修改,其中的操作数都是2e5范围内的数,这对我们就有一个暗示(赛时也往这方面想了的,不过还是对数论的学习不够到位),我们可以打一个素数表,这个素数表divs表示2e5以内的每个数的最小素因子,i.e.divs[x]表示x的最小素因子,这个可以随便找一个筛法就可,官方题解给的是优化了的埃氏筛法,时间复杂度是o(nloglogn),我下面给的是时间复杂度稍微更小一点的欧拉筛o(n),当然更优秀的筛法也有e.g.杜教筛啥的,不过对于此题没必要,埃氏筛法就可。然后就是维护n个数每个数的质因子以及指数,其中题目数据范围1<=n<=2e5,但是由于每个数的质因子数目以及种类可能各异,简单的用二维数组维护可能会MLE,因此可以将第二维离散化,即用一个map映射来维护;又由于质因数分解后求gcd就是求n个数每个质因数的最小指数,因此可以用pcnt[x]来维护n个数的x质因子中的指数(由于不管是初始n个数还是之后的乘法操作数都是2e5以内的,所以开比2e5大一点的空间即可),官方题解用到了multiset来存,即pcnt为multiset数组。
每个询问的时间复杂度为log级别(官方题解的叙述"Each query is processed in the complexity of the amount of prime divisors multiplied by the time of map and multiset operation, i.e. log.")
我的代码如下(很大程度模仿官方):
#pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; #define IOS std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0); typedef long long ll; typedef unsigned long long ull; struct InputOutputStream { enum { SIZE = 1000001 }; char ibuf[SIZE], *s, *t, obuf[SIZE], *oh; bool eof; InputOutputStream() : s(), t(), oh(obuf), eof(false) {} ~InputOutputStream() { fwrite(obuf, 1, oh - obuf, stdout); } explicit operator bool() const { return static_cast<bool>(eof == false); } inline char read() { if (s == t) t = (s = ibuf) + fread(ibuf, 1, SIZE, stdin); return s == t ? -1 : *s++; } inline InputOutputStream &operator>>(char* x) { static char c; for (c = read(); isspace(c); c = read()) if (c == -1) {eof = true; return *this;} for (; !isspace(c); c = read()) *x = c, ++x; *x = 0; return *this; } template <typename T> inline InputOutputStream &operator>>(T &x) { static char c; static bool iosig; for (c = read(), iosig = false; !isdigit(c); c = read()) { if (c == -1) {eof = true; return *this;} iosig |= c == '-'; } for (x = 0; isdigit(c); c = read()) x = x * 10 + (c ^ '0'); if (iosig) x = -x; return *this; } inline void print(char c) { if (oh == obuf + SIZE) { fwrite(obuf, 1, SIZE, stdout); oh = obuf; } *oh++ = c; } template <typename T> inline void print(T x) { static int buf[23], cnt; if (x != 0) { if (x < 0) print('-'), x = -x; for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 | 48; while (cnt) print((char)buf[cnt--]); } else print('0'); } template <typename T> inline InputOutputStream &operator<<(const T &x) { print(x); return *this; } inline void print(const char* x) { for(; *x; x++) print(*x); } inline void print(char* x) { for(; *x; x++) print(*x); } } io; const int mod = 1e9 + 7; const int maxn = 2e5 + 50; const int max_val = 2e5 + 50; int prime[max_val], id = 0; int divs[max_val]; void euler() { memset(divs, 0, sizeof(divs)); divs[1] = 1; for(int i = 2;i <= 200000;++i) { if(divs[i] == 0) { prime[++id] = i; divs[i] = i; } for(int j = 1;j <= id && i * prime[j] <= 200000;++j) { divs[i*prime[j]] = prime[j]; if(i % prime[j] == 0) { break; } } } } int n, q; ll ans = 1; multiset<int> pcnt[max_val]; map<int, int> cnt_divs[maxn]; void modify(int x, int val) { while(val != 1) { int div = divs[val], k = 0; while(divs[val] == div) { k++; val /= div; } int lst_cnt = cnt_divs[x][div]; int lst_min = 0; if((int)pcnt[div].size() == n) { lst_min = *(pcnt[div].begin()); } if(lst_cnt != 0) { pcnt[div].erase(pcnt[div].find(lst_cnt)); } cnt_divs[x][div] += k; pcnt[div].emplace(cnt_divs[x][div]); if((int)pcnt[div].size() == n) { for(int i = lst_min + 1;i <= (*pcnt[div].begin());++i) { ans = ans * div % mod; } } } } signed main() { io >> n >> q; euler(); for(int i = 1;i <= n;++i) { int val; io >> val; modify(i, val); } while(q--) { int x, val; io >> x >> val; modify(x, val); io << ans << '\n'; } return 0; }