• HDU5726:GCD——题解


    题目:hdu的5726

    (我原博客的东西,正好整理过来,属于st表裸题)
    (可以看出我当时有多么的菜……)
    这道题写了一遍,然而蒟蒻的我的时间爆炸了……
    于是看了一下学长的代码(顺便在此处%一下学长)。
    不明觉厉了两个小时
    终于看明白了
    由于这道题是基于st表写的(这部分比较基础)
    我就直接讲第二问(就是查相等GCD个数)
    那么为了不用每一个区间挨个比较一遍的话,我们所能想到的速度较快的方法……
    对!二分。
    但是如果把二分的方法想象成类似于查询数的方法那是不可以的(因为数字是无序的,很容易造成GCD增长趋势无规律,所以这个方法行不通)
    然后怎么办呢?
    这时候,如果我们固定左端点,存下左端点的值,然后去移动右端点的话,我们会发现什么呢?
    对,没错!我们发现了突破口!
    以这种方式移动而形成的区间的GCD永远要小于等于咱们存的值(这个不用讲为什么也应该能明白吧(即GCD永远不会大于整个区间内最小的数))
    也就是说GCD的值成单调不上升趋势。
    好的,然后我们怎么办呢?固定左端点,慢慢移动右端点来算?
    不用不用,我们用二分的思想,让它跳跃查询。
    (以下话请对照程序看)
    首先我们需要两个指针,取它们的mid,然后比较
    如果从左端点(即i)到mid的区间恰好等于所存值(即g),那么就说明有这个区间的长度数个相同的GCD。
    此时我们就可以将mid稍稍往右移一些,看看是否个数还能再增加(通过右移l实现)
    如果增加不了的话,就说明mid太大了,往左移一些(通过左移r实现)
    直到两个指针碰在一起了,算出个数(注意是将个数加上(+=)而不是赋值(=))
    然后呢将我们所存值(g)缩小一些(即移动j,然后g=suan(i,j))(因为此时mid=l,将j放在l的外面的话g恰好不一样(而且肯定变小))
    再重复上述步骤就可以了,我们此时就能得到所有GCD相同的个数了
    注意:因为本体数据过大,所以用map,具体怎么实现……还是看代码吧,我也不太会(也就是说,其实就是将二分的方法和map复述一遍罢了……这方法不是我原创的但题解是我写的(来自强迫症的我))

    附上代码:

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<map>
    using namespace std;
    map<int,long long>mp;
    int gcd(int a, int b){
    	if(!b)return a;
    	else return gcd(b,a%b);
    }
    int dp[100001][30];
    int qpow(int a){
    	return (1<<a);
    }
    void st(int n){
    	for(int j=1;j<=int(log2(n));j++){
    		for(int i=1;i<=n;i++){
    			int p=i+qpow(j)-1;
    			if(p>n)continue;
    			dp[i][j]=gcd(dp[i][j-1],dp[i+qpow(j-1)][j-1]);
    		}
    	}
    	return;
    }
    int suan(int x,int y){
    	double s=y-x+1;
    	int ke=log2(s);
    	int he=qpow(int(log2(s)));
    	return gcd(dp[x][ke],dp[y-he+1][ke]);
    }
    void erfen(int n){
    	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)/2;
    				if (suan(i,mid)==g)l=mid;
    				else r=mid-1;
    			}
    			mp[g]+=r-j+1;
    			j=l+1;
    			g=suan(i,j);
    		}
    	}
    }
    int main(){
    	int n,t,q,x,y,w;
    	scanf("%d",&t);
    	for(int wohaocai=1;wohaocai<=t;wohaocai++){
    		printf("Case #%d:
    ",wohaocai);
    		memset(dp,0,sizeof(dp));
    		scanf("%d",&n);
    		for(int i=1;i<=n;i++){
    			scanf("%d",&dp[i][0]);
    		}
    		st(n);
    		erfen(n);
    		scanf("%d",&q);
    		for(int i=1;i<=q;i++){
    			scanf("%d%d",&x,&y);
    			if(y<x){
    				int t=x;x=y;y=t;
    			}
    			int k=suan(x,y);
    			printf("%d %lld
    ",k,mp[k]);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    前缀和问题
    AtCoder Beginner Contest 085(ABCD)
    73.链表的基本操作
    112、文本串的加密
    100.容器List-ArrayList
    GUI颜色、字体设置对话框
    (贪心)多机调度问题
    POJ-1700 Crossing River
    lower_bound() upper_bound()函数
    HDU 1141
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8046799.html
Copyright © 2020-2023  润新知