zoukankan      html  css  js  c++  java
  • BZOJ 3813 奇数国

    线段树+数论

    看完这么一长串题目,现将有用的信息提取出来

    首先$number*x+product*y=1$这个条件

    如果要使$x$,$y$有解,那么$number$和$product$必须满足$gcd (number,product)=1$

    那么将询问操作转化为求出$[1,product]$中有多少个数与$product$互质

    那么这个就是求$varphi (product)$

    因为欧拉函数的通项公式$varphi (x)=xprod_{i=1}^{k}(1-frac{1}{p_{i}})$,p为x的质因数

    观察题目,题目保证所有的钱都是由前60个素数相乘得到的

    那么只要求出$product$就能通过逆元求出答案,19961993是质数,那么费马小定理即可

    因为求$product$是区间的操作,所以用线段树维护每个银行的所存钱数

    在线段树中要记录区间的乘积和当前区间出现的质数

    因为只有60个质数,可以状压到一个long long中保存

    那么pushup时也方便,直接将左儿子和右儿子的状态或运算即可

    那么接下来的问题就是线段树单点修改,区间询问求出$product$了

    最后要注意开long long时,常数也要转成long long(查这个查了一个小时)

    #include <bits/stdc++.h>
    #define mod (long long)19961993
    using namespace std;
    const long long MAXN=100100;
    long long n,p[61],w,in[61];
    struct node
    {
        long long l,r;
        long long mask,mul;
    }sh[MAXN*4];
    bool check(long long x)//判断质数
    {
        for (long long i=2;i*i<=x;i++)
        {
            if (x%i==0)
              return false;
        }
        return true;
    }
    long long split(long long x)//质因数分解
    {
        long long m=0;
        for (long long i=0;i<w;i++)
        {
            if (x%p[i]==0)
              m|=(1ll<<i);//注意
        }
        return m;
    }
    long long m_pow(long long a,long long b)
    {
        long long ans=1;
        while (b>0)
        {
            if (b&1)
              ans=(ans*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        return ans%mod;
    }
    long long inv(long long x)//用费马小定理求出逆元
    {
        return m_pow(x,mod-2)%mod;
    }
    void pushup(long long x)
    {
        sh[x].mul=(sh[x+x].mul*sh[x+x+1].mul)%mod;
        sh[x].mask=sh[x+x].mask|sh[x+x+1].mask;
    }
    void build(long long x,long long ll,long long rr)
    {
        sh[x].l=ll;
        sh[x].r=rr;
        if (ll==rr)
        {
            sh[x].mul=(long long)3;//注意
            sh[x].mask=split((long long)3);
            return;
        }
        long long mid;
        mid=(ll+rr)>>1;
        build(x+x,ll,mid);
        build(x+x+1,mid+1,rr);
        pushup(x);
    }
    void change(long long x,long long wh,long long v)//线段树单点修改
    {
        if (sh[x].l==wh && sh[x].r==wh)
        {
            sh[x].mul=v;
            sh[x].mask=split(v);
            return;
        }
        long long mid;
        mid=(sh[x].l+sh[x].r)>>1;
        if (wh<=mid)
          change(x+x,wh,v);
        else
          change(x+x+1,wh,v);
        pushup(x);
    }
    long long query1(long long x,long long ll,long long rr)//查找乘积
    {
        if (sh[x].l>=ll && sh[x].r<=rr)
          return sh[x].mul;
        long long mid;
        long long ans=1;
        mid=(sh[x].l+sh[x].r)>>1;
        if (ll<=mid)
          ans=(ans*query1(x+x,ll,rr))%mod;
        if (rr>mid)
          ans=(ans*query1(x+x+1,ll,rr))%mod;
        pushup(x);
        return ans;
    }
    long long query2(long long x,long long ll,long long rr)//查找区间质数出现情况
    {
        if (sh[x].l>=ll && sh[x].r<=rr)
          return sh[x].mask;
        long long mid;
        long long ma=0;
        mid=(sh[x].l+sh[x].r)>>1;
        if (ll<=mid)
          ma=ma|query2(x+x,ll,rr);
        if (rr>mid)
          ma=ma|query2(x+x+1,ll,rr);
        pushup(x);
        return ma;
    }
    int main()
    {
        scanf("%lld",&n);
        for (long long i=2;i<=281;i++)
        {
            if (check(i))
            {
                p[w]=i;//预处理出前60个素数
                w++;
            }
        }
        for (int i=0;i<w;i++)
          in[i]=inv(p[i]);//及其逆元
        build(1,1,100000);
        while (n--)
        {
            long long a,b,c;
            scanf("%lld%lld%lld",&a,&b,&c);
            if (a==0)
            {
                long long pro,m;
                long long tot=1;
                pro=query1(1,b,c);
                m=query2(1,b,c);
                for (long long i=0;i<w;i++)
                {
                    if ((m>>i)&1)
                      tot=(tot*(p[i]-1)%mod)*in[i]%mod;//欧拉函数通项公式
                }
                printf("%lld
    ",(pro*tot)%mod);
            }
            else
            {
                change(1,b,c);
            }
        }
    }
  • 相关阅读:
    Java学习资源整理(超级全面)
    Java中的Iterable与Iterator详解
    git使用笔记1:结合Github远程仓库管理项目
    关于LeetCode上链表题目的一些trick
    关于链表中哨兵结点问题的深入剖析
    关于Java中基类构造器的调用问题
    大整数相乘问题总结以及Java实现
    快速排序实现及其pivot的选取
    阿里云服务器部署Java Web项目全过程
    Git 学习总结
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11279111.html
Copyright © 2011-2022 走看看