首页 > 代码库 > hdu 5869 区间gcd的求法及应用
hdu 5869 区间gcd的求法及应用
题意:长度n的序列, m个询问区间[L, R], 问区间内的所有连续子段的不同GCD值有多少种.
题解:
1.因为n个数的gcd等于前n-1个数的gcd值再于第n个数gcd一下的值,再加上如果固定终点,区间向前延伸越多gcd必定是非严格递减的,所以我们可以预处理出以每一个数为终点的所有的后缀的gcd,每次求出第i个数为终点后记录下所有的gcd值再与第i+1个数求gcd,这个很好做。
2.将所有的查询按右区间从小到大排序,同时将第一步记录的值在树状数组中更新,然后用树状数组区间求和就好了,这一步在代码里面解释
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> #pragma comment(linker, "/STACK:102400000,102400000")using namespace std;#define PF(x) cout << "debug: " << x << " ";#define EL cout << endl;#define PC(x) puts(x);typedef long long ll;#define CLR(x, v) sizeof (x, v, sizeof(x))using namespace std;const int INF = 0x5f5f5f5f;const int maxn = 2e5 + 10;const int mod = 1e9 + 7;const double eps = acos(-1);const double PI= atan(1.0)*4;int n,q,a[maxn],tree[maxn],vis[2000000],ans[maxn];vector<pair<int,int> >gd[maxn];struct st{ int l,r,id;}qer[maxn];int gcd(int a,int b){ return b == 0?a:gcd(b,a%b);}bool cmp(st x,st y){ return x.r < y.r;}void Add(int k,int num){ while(k <= n){ tree[k] += num; k += k&(-k); }}int Sum(int k){ int sum = 0; while(k > 0){ sum += tree[k]; k -= k&(-k); } return sum;}int main(){ freopen("in.txt","r",stdin); while(~scanf("%d%d",&n,&q)){ for(int i = 1;i <= n;i++){ scanf("%d",&a[i]); gd[i].clear(); } for(int i = 1;i <= n;i++){ int x = a[i],y = i; for(int j = 0;j <gd[i-1].size();j++){ int t = gcd(gd[i-1][j].first,a[i]); if(t != x){ gd[i].push_back(make_pair(x,y)); x = t,y = gd[i-1][j].second;//x记录从y位置到当前处理的i位置的gcd值,y为相同gcd中最右边的位置,最右边这个是必须的 } } gd[i].push_back(make_pair(x,y)); } for(int i = 1;i <= q;i++){ scanf("%d%d",&qer[i].l,&qer[i].r); qer[i].id = i; } sort(qer + 1,qer + 1 + q,cmp); memset(vis,0,sizeof(vis)); memset(tree,0,sizeof(tree)); int len = 1; for(int i = 1; i <= n;i++){ // cout<<i<<":"; for(int j = 0;j < gd[i].size();j++){ // cout<<gd[i][j].first<<"->"<<gd[i][j].second<<" "; int c1 = gd[i][j].first,c2 = gd[i][j].second; //如果这个gcd值c1出现过则更新其位置,根据非严格递减, 这样一定能更新为最靠近当前的i的位置 if(vis[c1] > 0) Add(vis[c1],-1); vis[c1] = c2; Add(c2,1); } // cout<<endl; while(qer[len].r == i){ ans[qer[len].id] = Sum(qer[len].r) - Sum(qer[len].l-1); len++; } } for(int i = 1;i <= q;i++) printf("%d\n",ans[i]); } return 0;}
可以参考博客http://blog.csdn.net/angon823/article/details/52503408
hdu 5869 区间gcd的求法及应用
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。