[ARC023D] GCD区間 题解
[ARC023D] GCD区間
upd on 2024.1.26:修正了 ST 表的时间复杂度分析。
一道数据结构维护
首先,大家应该都能想到一种大暴力:枚举左右端点
复杂度
很明显,
但是这道题涉及到了区间求
所以我们简单用一个 ST 表来维护
template<typename Tp, size_t siz> struct ST { Tp _con[siz][21]; Tp& operator[](int i) {return _con[i][0];} void pre() { for(int i=1;i<=20;i++) for(int j=1;j+(1<<i)-1<=siz;j++) _con[j][i]=gcd(_con[j][i-1], _con[j+(1<<(i-1))][i-1]); } Tp query(int l, int r) { if(l>r) return 0; int len=lgs[r-l+1]; return gcd(_con[l][len], _con[r-(1<<len)+1][len]); } };
但是还是卡不过去啊
考虑这样一个序列:12 8 4 6 4 3
。
当 12 4 4 2 2 1
。
我们发现该题的
一定时, 单调递减。
所以我们想到二分答案。
二分一段相同
时间复杂度
Code
#include<bits/stdc++.h> #include<bits/extc++.h> #define pb __gnu_pbds using namespace std; template< typename T >inline void read(T &x) { char c=getchar();x=0;int f=0; for(;!isdigit(c);c=getchar()) f|=(c=='-'); for(;isdigit(c);c=getchar()) x=((x<<3)+(x<<1)+(c^48)); x=f?-x:x; } template< typename T >inline void write(T x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10^48); } template< typename T,typename ... Args > inline void read(T &_x, Args &... args) {read(_x), read(args...);} template< typename T,typename ... Args > inline void write(T _x, Args ... args) { write(_x), write(args...);} //快读快写 pb::gp_hash_table<int,long long> gcdct; //用一个哈希表存储 gcd 为某值的对数 inline int gcd(int a,int b) { if(!(a&&b)) return a|b; int alow=__builtin_ctzll(a), blow=__builtin_ctzll(b); int low=(alow<blow)?alow:blow; int c=0; b>>=blow; while(a) { a>>=alow; c=b-a; alow=__builtin_ctzll(c); if(a<b) b=a; a=(c<0)?-c:c; } return b<<low; } // Stein gcd 更相减损法求 gcd int lgs[100005]; void prel() { for(int i=2;i<=100000;i++) lgs[i]=lgs[(i>>1)]+1; } // 预处理 log2(n) template<typename Tp, size_t siz> struct ST { Tp _con[siz][21]; Tp& operator[](int i) {return _con[i][0];} void pre() { for(int i=1;i<=20;i++) for(int j=1;j+(1<<i)-1<=siz;j++) _con[j][i]=gcd(_con[j][i-1], _con[j+(1<<(i-1))][i-1]); } Tp query(int l, int r) { if(l>r) return 0; int len=lgs[r-l+1]; return gcd(_con[l][len], _con[r-(1<<len)+1][len]); } }; ST<int, 100005> lis; // 相当简单的 ST 表,维护区间 gcd inline int find(int l, int r0, int n) { int g0=lis.query(l, r0); int ret=0, r=n; while(r0<=r) { int mid=(r0+r)>>1; if(lis.query(l, mid)==g0) r0=mid+1, ret=mid; else r=mid-1; } return ret; } // 二分查找右端点 signed main() { int n,q; read(n, q); prel(); for(int i=1;i<=n;i++) read(lis[i]); lis.pre(); // 读入和预处理部分 for(int l=1;l<=n;l++) { int ls, nr=l; for(;nr<=n;) { ls=nr; nr=find(l, nr, n); gcdct[lis.query(l, ls)]+=nr-ls+1; nr++; } // 枚举左端点,统计答案 } while (q--) { read(n); write(gcdct[n]); putchar(10); // O(1)查询 } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379516
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步