『HDU 5726』GCD

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5726

个人感想: 这道题呢我们在比赛的时候想到过用线段树来处理.可是有个问题的就是我们队伍并不知道该如何去处理查线段树的整个区间的的个数.我队友提出过用合并区间…我觉得不可能啊,怎么合并,类似于这样

这里写图片描述
因为我已经知道前三个数以4结尾的3个区间,当我新加一个6时,就得和前面求出来的GCD值再求GCD值,然后就得到以6为结尾的前4个区间的GCD的值,每次处理只要用个Set存起来就好了. 可是这样子最差的情况会出现(n^2) 然后我们就直接放弃了…
后来大牛跟我们说绝对不会出现(n^2)的情况,因为求到的GCD值会越来越小,处理出来相同的GCD值合并起来,就可以降低复杂度,!反正不会超过logn就可以处理完…你问我为什么?我也不太情况,个人感觉写了很多种样例都能合并起来,不会有合并不起来的结果,可能的GCD值只会在这些数得范围内,一旦遇到一个素数,那么前边的区间必定都成1了,肯定能合并起来,如果没有素数,就算全是偶数,反正也不会超过logn,至于证明我就不太会了,毕竟是个渣渣,如果有哪位大牛可以告诉我下那就更好了…
对了记得要全部换成long long 才行,不换long long 是过不了的,可是我觉得以题目的数据范围,int绝对可以了队友说,可能是int 的值接近爆炸时 取余的时候会出现问题…–可能成立吧,我倒没试过,暂时只能这样理解了

分析:线段树+预处理
代码:

/* Author:GavinjouElephant
 * Title:
 * Number:
 * main meanning:
 *
 *
 *
 */

//#define OUT
#include <iostream>
using namespace std;
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <vector>
#include <set>
#include <cstdlib>
#include <map>
#include <queue>
//#include<initializer_list>
//#include <windows.h>
//#include <fstream>
//#include <conio.h>
#define MaxN 0x7fffffff
#define MinN -0x7fffffff
#define Clear(x) memset(x,0,sizeof(x))
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
typedef long long ll;
ll T;
ll N;
ll Q;
ll X[maxn];
map<ll,ll> Set;
map<ll,ll> temp;
map<ll,ll> Cou;

class QLR
{
public:
     ll L;
     ll R;
};
QLR qlr[maxn];
class Node
{
public:
    ll l;
    ll r;
    ll w;
};
Node node[4*maxn];
ll gcd(ll a,ll b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
void Build_Tree(ll l,ll r,ll k)
{
    node[k].l=l;
    node[k].r=r;
    if(l==r)
    {
        node[k].w=X[l];
        return;
    }
    ll mid=(l+r)/2;
    Build_Tree(l,mid,2*k);
    Build_Tree(mid+1,r,2*k+1);

    node[k].w=gcd(node[2*k].w,node[2*k+1].w);
    return;
}
ll Search_Tree(ll l,ll r,ll k)
{
    ll a=0;
    ll b=0;
    if(l<=node[k].l&&node[k].r<=r)
    {
        return node[k].w;
    }
    ll mid=(node[k].l+node[k].r)/2;
    if(l<=mid)
    {
        a=Search_Tree(l,min(mid,r),2*k);
    }
    if(r>mid)
    {
        b=Search_Tree(max(mid+1,l),r,2*k+1);
    }
    return gcd(a,b);
}
int main()
{
#ifdef OUT
    freopen("coco.txt","r",stdin);
    freopen("lala.txt","w",stdout);
#endif
     ll cas=0;
     scanf("%I64d",&T);
     while(T--)
     {
         Cou.clear();
         temp.clear();
         Set.clear();
         cas++;
         printf("Case #%I64d:\n",cas);
         scanf("%I64d",&N);
         for(ll i=1;i<=N;i++)
         {
             scanf("%I64d",&X[i]);
         }
         Build_Tree(1,N,1);

         scanf("%I64d",&Q);
         for(ll i=1;i<=Q;i++)
         {
            scanf("%I64d%I64d",&qlr[i].L,&qlr[i].R);
         }

         for(ll i=1;i<=N;i++)
         {

              temp.clear();
              for(map<ll,ll>::iterator it=Cou.begin();it!=Cou.end();it++)
              {
                  ll t=(*it).first;
                  ll k=gcd(t,X[i]);
                  temp[k]+=Cou[t];
              }
              Cou.clear();
              for(map<ll,ll>::iterator it=temp.begin();it!=temp.end();it++)
              {
                  Cou[(*it).first]=temp[(*it).first];
              }

              for(map<ll,ll>::iterator it=Cou.begin();it!=Cou.end();it++)
              {
                  Set[(*it).first]+=Cou[(*it).first];
              }
              Set[X[i]]+=1;
              Cou[X[i]]+=1;
         }
         for(ll i=1;i<=Q;i++)
         {
             ll k=Search_Tree(qlr[i].L,qlr[i].R,1);
             printf("%I64d %I64d\n",k,Set[k]);
         }

     }


    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值