hdu 5726 区间gcd RMQ+二分 || 暴力枚举
来源:互联网 发布:傲虎网络 编辑:程序博客网 时间:2024/05/07 18:35
题意:
给定一个序列,每次询问一个区间
输出这个区间上所有数的GCD,以及GCD与其相同的区间个数
思路:
首先我们要考虑到gcd随着区间长度的增加是非递增的,并且任何一个区间gcd的个数是log级别的 。所以对于这个题目我们可以固定右端点,然后枚举左端点,统计不同的gcd的个数,同时用map记录gcd的数量即可.
用一个vector 对应两个键值一个是区间的gcd,一个是从第i个位置往前所能扩张到的最远下标。由gcd性质可知,vector存的gcd是非递增的.而且当第i个数作为右端点时,第i-1个数已经做过了,那么只需要利用第i-1个的把第i的数a[i]加入即可。
复杂度
#include<bits/stdc++.h>#define pb push_back#define mk make_pair#define P pair<int,int> using namespace std;typedef long long ll;const int maxn = 1e5 + 5;int n,q;int a[maxn];vector<P >vt[maxn];map<int,ll>mp;int main(){ int _; cin>>_; int ca = 1; while(_--) { int len; mp.clear(); scanf("%d",&n); for(int i = 1;i <= n; ++i) scanf("%d",a + i),vt[i].clear(); for(int i = 1;i <= n;++i) { mp[a[i]]++; vt[i].pb(mk(a[i],i)); len = 0; for(int j = 0;j < vt[i-1].size() ;++j) { int tmp = __gcd(a[i],vt[i-1][j].first); mp[tmp] += vt[i][len].second - vt[i-1][j].second; if(tmp == vt[i][len].first)//由递减性质只可能和前一个相同. vt[i][len].second = vt[i-1][j].second; else vt[i].pb(mk(tmp,vt[i-1][j].second)),len++; } } scanf("%d",&q); printf("Case #%d:\n",ca++); while(q--) { int l,r; int ans; scanf("%d %d",&l,&r); if(l >= vt[r][0].second) ans = vt[r][0].first; else { for(int i = 1;i < vt[r].size(); ++i) { if(vt[r][i-1].second > l && l >= vt[r][i].second) { ans = vt[r][i].first; break; } } } printf("%d %lld\n",ans,mp[ans]); } } return 0; }
当然,一般处理区间gcd的问题我们可以想到RMQ.和普通的处理区间最值一样,这里的RMQ只是增加了一个gcd而已并没有什么影响.
我们还是根据区间gcd的性质,gcd的个数最多为log级别的,那么我们可以预先处理好所有可能的gcd,然后直接输出即可.
这个过程我们先枚举每个点gcd作为左端点,然后去二分右端点(因为gcd的性质使得gcd具有单调性),那么我们最多会有log个不同的gcd.总体的复杂度为
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int maxn=1e5+10;int n,q;int dp[maxn][20],lg2[maxn];map<int,ll>mp;void ST(){ for(int i = 2;i <= n;++i) lg2[i] = lg2[i/2]+1; for(int j = 1;(1 << j) <= n;++j) { for(int i = 1;i + (1 << j) - 1 <= n;++i) { dp[i][j] = __gcd(dp[i][j-1] ,dp[(1<<(j-1))+i][j-1]); } } return ;}int query(int l,int r){ int k = lg2[r-l+1]; return __gcd(dp[l][k],dp[r-(1<<k)+1][k]);}void solve(){ mp.clear(); for(int i = 1;i <= n;++i) { int g = dp[i][0],j = i; while(j <= n) { int l = j,r = n; while(l <= r) { int mid = (l + r)>> 1; if(query(i,mid) == g) l = mid+1; else r= mid - 1; } mp[g] += (r-j+1); j = r + 1; g = query(i,j); } }}int main(){ int _; cin>>_; int ca = 1; while(_--) { scanf("%d",&n); for(int i = 1;i <= n;++i) scanf("%d",&dp[i][0]); ST(); solve(); scanf("%d",&q); printf("Case #%d:\n",ca++); while(q--) { int l,r; scanf("%d %d",&l,&r); int gcd = query(l,r); printf("%d %lld\n",gcd,mp[gcd]); } } return 0;}
阅读全文
0 0
- hdu 5726 区间gcd RMQ+二分 || 暴力枚举
- hdu 5726 GCD RMQ+二分枚举
- HDU 5726 GCD [RMQ+二分]
- HDU 5726 GCD(RMQ + 二分)
- Hdu-5726 GCD (二分 + RMQ)
- HDU 5726 GCD (RMQ + 二分)
- hdu 5726 GCD rmq 二分
- hdu 5726 GCD (区间gcd-RMQ)
- HDU 5726 GCD(枚举暴力)
- HDU 5726 GCD (rmq+二分 or 线段树 维护区间gcd)
- hdu 5726 GCD (区间RMQ )
- HDU 5726 GCD 【GCD】【ST表+二分】【线段树+暴力枚举】
- HDU 5726 求gcd=k的区间的个数 (二分+RMQ)
- HDU 5726 GCD(RMQ+二分,详解)
- HDU 5726 GCD(RMQ+二分)
- HDU 5726-D-GCD- RMQ+二分
- HDU 5726 GCD(rmq+二分)
- HDU 5726 GCD(RMQ 查询 + 二分)
- 2017/9/21
- 面向对象(继承)
- HDU 4641 至少出现K次本质不同子串数:后缀自动机
- UVa1629 记忆化搜索 分格子
- 两个有序链表序列的合并
- hdu 5726 区间gcd RMQ+二分 || 暴力枚举
- 算法训练 区间k大数查询
- HIBERNATE与 MYBATIS的对比
- 两个有序链表序列的交集
- 趋势科技面试总结
- HashMap的数据结构分析
- leetcode题目例题解析(四)
- 树的广度深度优先遍历算法 DFS BFS
- hdu 1009 FatMouse' Trade