HDU 5726 GCD 区间gcd查询 MAP RMQ 优化
来源:互联网 发布:ubuntu 邮件阅读器 编辑:程序博客网 时间:2024/05/22 02:40
原题见HDU 5726
给N(
分析
查询次数很多,要做预处理,用map<最大公约数,区间数>存下来,实现O(1)的查询。
预处理发现对于同一左端点的区间而言,右端点越靠右,区间gcd单调递减。因此可以固定左端点,二分右端点,找到gcd突变的右端点,确定对于同一gcd的区间数目。为了查询得更快,用ST表(RMQ)存下区间gcd,只要O(1)即可查询得到区间gcd。
操作
RMQ建表
即区间
for(int i = 0;i < n;i++){ dp[i][0] = a[i];}for(int j = 1;(1<<j) <= n;j++){ for(int i = 0;i + (1<<j) - 1 < n;i++){ dp[i][j] = __gcd(dp[i][j-1], dp[i+(1<<j-1)][j-1]); }}
RMQ询问
int ask(int l, int r){ int k = log(1.0*(r-l+1))/log(2); return __gcd(dp[l][k], dp[r-(1<<k)+1][k]);}
据MrBird_to_fly回忆,还有一种对
int k=31-__builtin_clz(r-l+1);
int __builtin_ctz (unsigned int x)
返回右起第一个‘1’之后的0的个数。
统计预处理
map<int, int> mp;int find(int x, int g){ int l = x, r = n-1; if(ask(x, r) > g) return r+1; if(ask(x, l) <= g) return l; int cnt = 20; while(l < r && cnt--){ int mid =l+r >> 1; if(ask(x, mid) > g) l = mid; else r = mid; } if(ask(x, l) > g) return r; return l;}void makeST(){ mp.clear(); for(int i = 0;i < n;i++){ for(int j = i;j < n;){ int g = ask(i, j); int k = find(i, g-1); mp[g] += k-j; j = k; } }}
优化
上面的预处理左端点逐个枚举,其实重复查询了很多个转折点。预处理复杂度为O(nlog(a)log(a))(a<=10^9)
现在先固定区间右端点,左端点从下标0处开始向右,直到与右端点重合,这过程中gcd逐渐增大,并且也存在转折点。
右端点右移,则可以利用上一个右端点的转折点得到现在的转折点。上一次的转折点要么被舍去(两边的gcd值相等),要么继续保留,但不会再增加(除了右端点作为转折点)。
举例:10 20 3 15 1000 60 16
每次都以右端点和上一次的转折点对应的gcd得到gcd,只有得到不同gcd才将该转折点保留下来,否则舍去。比如右端点为3时,10,20和3的最大公约数都为1,只需保留10.
需注意的是,右端点和上一次转折点的gcd并不是右边这一栏的gcd。如右端点为15时,转折点为10,为什么对应的gcd为1呢?这是由于1是[10,20,3]的gcd,所以[10,20,3,15]的gcd只能是1的约数。
优化后的复杂度为O(nlog(a))
typedef map <int, LL> MAP;typedef pair<int, int> PR;typedef vector<PR> DV;DV dv, tp;MAP mp;
void makeTable{ dv.clear(); for(int i = 0;i < n;i++){ int g = -1; tp.clear(); dv.push_back(PR(a[i], i)); for(DV::iterator it = dv.begin();it != dv.end();it++){ if(__gcd(it->first, a[i]) != g){ g = __gcd(it->first, a[i]); tp.push_back(PR(g, it->second)); } } tp.push_back(PR(0, i+1)); for(DV::iterator it = tp.begin();it+1 != tp.end();it++) mp[it->first] += (it+1)->second-it->second; swap(dv, tp); }}
应用
CF 475D. CGCDSSQ
求最大公约数为g的区间数。
数据范围和上面那题差不多,但是时间只有2s,不优化就会T(也许是写得搓)。
预处理优化 142ms
log优化 436ms
- HDU 5726 GCD 区间gcd查询 MAP RMQ 优化
- hdu 5726 GCD (区间gcd-RMQ)
- hdu 5726 GCD (区间RMQ )
- hdu 5726 gcd rmq 求相同gcd的区间数量
- HDU 5726 GCD(RMQ 查询 + 二分)
- HDU 5726 GCD RMQ
- hdu 5726 区间gcd RMQ+二分 || 暴力枚举
- hdu 5726(区间gcd)
- HDU 5726 GCD (rmq+二分 or 线段树 维护区间gcd)
- HDU 5726 GCD [RMQ+二分]
- HDU 5726 GCD(RMQ + 二分)
- Hdu-5726 GCD (二分 + RMQ)
- HDU 5726 GCD (RMQ + 二分)
- hdu 5726 GCD rmq 二分
- hdu 5726 GCD 【RMQ+st】
- HDU 5726 GCD(RMQ+二分,详解)
- 【HDU 5726】GCD(映射+RMQ)
- HDU 5726 GCD(RMQ+二分)
- 范式
- 使用 foreach 操作数组
- 面试题2:实现Singleton模式
- 百度工程师讲PHP函数的实现原理及性能分析(一)
- linux的du和df命令
- HDU 5726 GCD 区间gcd查询 MAP RMQ 优化
- 百度工程师讲PHP函数的实现原理及性能分析(二)
- eCongnition中NN分类和规则分类理解
- JAVA CAS原理深度分析
- hdu g++开栈
- NKOI 1905 慢跑小路
- linux tail 命令动态显示日志信息
- 【CodeForces】611A - New Year and Days(水)
- 百度工程师讲PHP函数的实现原理及性能分析(三)