zoukankan      html  css  js  c++  java
  • Codeforces 1493D

    Codeforces Round #705 (Div.2) D - GCD of an Array


    题意

    给定长度为(n)的数组({a}),有(q)次操作与询问

    每次操作给定(i)(x),使得(a_i=a_i*x)

    每次操作后询问此时这个数组所有元素的最大公因数GCD是多少

    限制

    (1le nle 2cdot 10^5)

    (1le a_ile 2cdot 10^5)

    (1le ile n, 1le xle 2cdot 10^5)




    思路

    (好像有点卡常的样子,也可能是后面多加了几句判断优化掉了)

    如果觉得思路讲得有点乱的话可以直接参考代码,注释基本都写着


    众所周知,对于准备求GCD的所有数字进行质因子分解

    那么GCD的值就是每个质因子在所有数中出现的最小幂次的乘积

    以这一储备知识作为前提,开始讨论解题方案


    刚开始本来想的是使用线段树点修改来维护每个质因子(t)在每个位置的出现次数

    然后区间查询最小值来获取这个质因子目前的贡献(v)

    然后再开一个(pre)数组用于记录每个质因子最小幂次的前置状态

    由于我们不能每次都算一遍每个质因子的贡献,所以只能尝试去维护答案变量(ans)

    所以每次我们可以得到质因子(t)所作出贡献的幂次差值为(v-pre[d])

    故在当前点修改之后,需要将当前贡献加入答案,即让(ans)乘上(d^{v-pre[d]})

    但明显的,线段树空间复杂度严格为(O(4n))(2cdot10^5)以内存在(10^4)以上个素数

    换言之需要开(O(4cdot 10^4n))的空间来存线段树,显然不可行(存在大量空间冗余)

    所以从空间角度进行优化


    发现我们可以利用multiset的不查重以及自动排序的特点来维护最小值

    故可以令(st[d])容器来表示质因子(d)数组({a})中每个位置出现的幂次

    为了使其不存在像线段树那样造成的空间冗余,故如果在某个位置质因子(d)的幂次为(0),则不将其插入multiset中

    使用multiset容器,只需要判断(st[d].size())是否等于(n)就能得知是否在每个位置都有质因子(d)存在

    再通过(*st[d].begin())来获得最小值,再同上方法与(pre[d])做差计算答案

    这样就做到了类似线段树中“区间查询”的功能


    然后考虑multiset容器怎么做到“单点修改”的功能

    对于每个位置再引入一个map容器,使(mp[i][d])用于表示质因子(d)在位置(i)的幂次

    如果在操作过程中需要让(i)位置的质因子(d)的幂次加上(t)

    只需要先将原先表示的幂次(mp[i][d])(st[d])中删去

    再将现在表示的幂次(mp[i][d]+t)插入(st[d])即可(记得操作后让(mp[i][d])加上(t)

    这样便做到了“单点修改”的功能


    综上便能做到利用multiset代替线段树并减少空间冗余(为(0)则不分配空间原则)

    最后注意优化常数即可




    代码

    (Pretests 872ms/2500ms)

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=1e9+7;
    ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
    
    multiset<int> st[200050]; //记录每个因子在每个位置出现的次数,未出现则不需要插入
    int pre[200050]; //附加在st上,用于记录上一状态某质因子出现的次数,与st首元素的差值可用于计算答案
    map<int,int> mp[200050]; //记录每个位置每个质因子目前的幂次
    
    vector<int> primvec; //素数
    bool vis[200050];
    
    void init() //素数筛,素数存入primvec内
    {
        vis[0]=vis[1]=true;
        for(int i=2;i<=1000;i++)
        {
            if(!vis[i])
            {
                primvec.push_back(i);
                for(int j=i*i;j<=200000;j+=i)
                    vis[j]=true;
            }
        }
        for(int i=1001;i<=200000;i++)
            if(!vis[i])
                primvec.push_back(i);
    }
    
    void solve()
    {
        int n,q;
        cin>>n>>q;
        ll ans=1;
        
        for(int i=1;i<=n;i++)
        {
            int d;
            cin>>d;
            for(int j:primvec) //对于每个输入的数字,进行一次质因子分解
            {
                if(d==1) //直接跳出循环节省时间
                    break;
                if(!vis[d]) //如果可以直接判断为素数,尽量直接跳出循环
                {
                    mp[i][d]=1;
                    st[d].insert(1);
                    break;
                }
                int t=0;
                while(d%j==0) //分解质因子j,记录次数
                {
                    d/=j;
                    t++;
                }
                if(t)
                {
                    mp[i][j]=t; //第i个位置的质因子j的幂次为t
                    st[j].insert(t); //记录质因子j在每个位置出现的次数
                }
            }
        }
        
        for(int j:primvec)
        {
            if(st[j].size()==n) //如果质因子j在每个位置都出现至少一次
            {
                int tmp=*st[j].begin(); //此时最少的出现次数
                if(tmp!=pre[j]) //与pre做差,得到答案增幅
                {
                    ans=ans*qpow(j,tmp-pre[j])%mod;
                    pre[j]=tmp;
                }
            }
        }
        
        while(q--)
        {
            int p,d;
            cin>>p>>d;
            for(int j:primvec) //对d进行质因子分解
            {
                if(d==1) //此处优化同上
                    break;
                if(!vis[d])
                {
                    int &v=mp[p][d]; //直接引用以优化常数
                    if(v) //如果mp[p][d]不为0,说明质因子d已经有幂次,需要将其先从st[d]中删去
                        st[d].erase(st[d].lower_bound(v));
                    v++;
                    st[d].insert(v);
                    
                    if(st[d].size()==n) //如果质因子j在每个位置都出现至少一次,下同
                    {
                        int tmp=*st[d].begin();
                        if(tmp!=pre[d])
                        {
                            ans=ans*qpow(d,tmp-pre[d])%mod;
                            pre[d]=tmp;
                        }
                    }
                    break;
                }
                int t=0;
                while(d%j==0)
                {
                    d/=j;
                    t++;
                }
                if(t) //下同
                {
                    int &v=mp[p][j];
                    if(v)
                        st[j].erase(st[j].lower_bound(v));
                    v+=t; //应增加t次
                    st[j].insert(v);
                    
                    if(st[j].size()==n)
                    {
                        int tmp=*st[j].begin();
                        if(tmp!=pre[j])
                        {
                            ans=ans*qpow(j,tmp-pre[j])%mod;
                            pre[j]=tmp;
                        }
                    }
                }
            }
            cout<<ans<<'
    ';
        }
    }
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        init();
        solve();
        return 0;
    }
    

    https://blog.csdn.net/qq_36394234/article/details/114464389


  • 相关阅读:
    String,StringBuffer与StringBuilder的区别?
    Digui
    Digui1
    逆序
    TestOverWrite
    DemoBoxWeight
    TestSuperSub
    Cast
    TestOverWrite
    Joseph
  • 原文地址:https://www.cnblogs.com/stelayuri/p/14493186.html
Copyright © 2011-2022 走看看