• HDU 5869 Different GCD Subarray Query(2016大连网络赛 B 树状数组+技巧)


    还是想不到,真的觉得难,思路太巧妙

    题意:给你一串数和一些区间,对于每个区间求出区间内每段连续值的不同gcd个数(该区间任一点可做起点,此点及之后的点都可做终点)

    首先我们可以知道每次添加一个值时gcd要么不变要么减小,并且减小的幅度很大,就是说固定右端点时最多只能有(log2 a)个不同的gcd,而且我们知道gcd(gcd(a,b),c)=gcd(a,gcd(b,c)),所以我们可以使用n*(log2 n)的时间预处理出每个固定右端点的不同gcd的值和位置。解法就是从左到右,每次只需要使用上一次的不同gcd的值和位置。

    离线处理每个询问,按照区间右端点从小到大排序,把预处理的每个gcd依次加入树状数组中,这样到一个询问的右端点时就区间查询不同gcd的个数(只需要使用左端点的位置)。树状数组在每种gcd最右一个左端点的位置(贪心想法)存这儿gcd的个数,但可能这个gcd之前出现过,因此我们可以再开一个数组存每种gcd的位置(保证每种gcd在最右边的位置)

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define eps 1E-8
    /*注意可能会有输出-0.000*/
    #define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
    #define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
    #define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
    #define mul(a,b) (a<<b)
    #define dir(a,b) (a>>b)
    typedef long long ll;
    typedef unsigned long long ull;
    const int Inf=1<<28;
    const double Pi=acos(-1.0);
    const int Mod=1e9+7;
    const int Max=100010;
    struct node
    {
        int mpos,gcd;
    } dp[Max][50]; //预处理
    struct nide
    {
        int lef,rig,mpos;
    } qes[Max];
    int bit[Max],mpos[Max*10];//树状数组处理此位置元素个数 某个gcd的位置(这时的最靠右)
    int num[Max],ans[Max],n;
    void Swap(int &a,int &b)
    {
        a^=b;
        b^=a;
        a^=b;
        return;
    }
    int Gcd(int a,int b)// a、b 均为偶数, gcd(a,b) = 2 * gcd(a/2, b/2)
    {
        if(a<b)
            Swap(a,b);
        int c=1; //a为偶数,b为奇数, gcd(a,b) = gcd(a/2 , b)
        while(a-b) //a为奇数,b为偶数,gcd(a,b) = gcd(a, b/2)
        {
            if(a&1) //a、b均为奇数, gcd(a,b) = gcd((a-b)/2, b)
            {
                if(b&1)
                {
                    if(a>b) a=(a-b)>>1;
                    else  b=(b-a)>>1;
                }
                else b>>=1;
            }
            else
            {
                if(b&1) a>>=1;
                else  c<<=1,a>>=1,b>>=1;
            }
        }
        return c*a;
    }
    int cntt[Max];
    void Init()//预处理固定右端点时左边的g不通gcd
    {
        int tmp;
        cntt[0]=0;
        for(int i=1; i<=n; ++i) //固定右端点i
        {
            cntt[i]=1;
            dp[i][1].gcd=num[i];
            dp[i][1].mpos=i;
            for(int j=1; j<=cntt[i-1]; ++j)
            {
                tmp=Gcd(dp[i][cntt[i]].gcd,dp[i-1][j].gcd);
                if(tmp!=dp[i][cntt[i]].gcd)//与每个右端点形成不同的gcd的位置要尽量靠右
                {
                    dp[i][++cntt[i]].gcd=tmp;
                    dp[i][cntt[i]].mpos=dp[i-1][j].mpos;
                }
            }
        }
        return;
    }
    bool cmp(struct nide p1,struct nide p2)//排序右端点才可以统计
    {
        return p1.rig<p2.rig;
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void Add(int x,int y)
    {
        while(x<=n)
        {
            bit[x]+=y;
            x+=lowbit(x);
        }
        return;
    }
    int Sum(int x)
    {
        int sum=0;
        while(x)
        {
            sum+=bit[x];
            x-=lowbit(x);
        }
        return sum;
    }
    void Solve(int q)
    {
        memset(bit,0,sizeof(bit));
        memset(mpos,0,sizeof(mpos));
        int k=1,sum=0;
        for(int i=1; i<=n; ++i) //枚举右端点
        {
            for(int j=1; j<=cntt[i]; ++j)
            {
                if(!mpos[dp[i][j].gcd])
                {
                    sum++;
                    Add(dp[i][j].mpos,1);
                    mpos[dp[i][j].gcd]=dp[i][j].mpos;
                }
                else if(mpos[dp[i][j].gcd]<dp[i][j].mpos)//保证最右位置
                {
                    Add(mpos[dp[i][j].gcd],-1);
                    Add(dp[i][j].mpos,1);
                     mpos[dp[i][j].gcd]=dp[i][j].mpos;
                }
            }
            while(k<=q&&qes[k].rig==i)
            {
                ans[qes[k].mpos]=sum-Sum(qes[k].lef-1);//计算[lef,rig]的个数
                ++k;
            }
        }
        return;
    }
    int main()
    {
        int q;
        while(~scanf("%d %d",&n,&q))
        {
            for(int i=1; i<=n; ++i)
                scanf("%d",&num[i]);
            Init();
            for(int i=1; i<=q; ++i)
            {
                scanf("%d %d",&qes[i].lef,&qes[i].rig);
                qes[i].mpos=i;
            }
            sort(qes+1,qes+1+q,cmp);
            Solve(q);
            for(int i=1; i<=q; ++i)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
  • 相关阅读:
    使用Visual Studio .Net 做自己的汉化软件
    给所有的Control加两个属性,实现回车键自动跳转到下一个控件
    数字逗号标记—以前原创(一)
    解决w3wp.exe占用CPU和内存问题
    sql日期函数
    索引的使用总结
    w3wp.exe狂占内存
    w3wp.exe占内存CPU问题 WIN2003 IIS6.0假死现象的分析
    查看Linux系统日志
    linux动态增加LV空间
  • 原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/5869879.html
Copyright © 2020-2023  润新知