【清华集训2014】奇数国 题解(线段树+欧拉函数)

题目链接

题目大意:给出一段长度为$100000$的初始值为$3$的序列。有两种操作:1.把$a_x$的值改为$y$;2.求$varphi (prod _{i=l}^r a_i)mod 19961993$的值。

-----------------------------------

题目比较显然,可以用线段树维护区间积。

欧拉函数的通式:$varphi (n)=n*(1-frac{p_1-1}{p_1})*(1-frac{p_2-1}{p_2})*cdots *(1-frac{p_k-1}{p_k}),n=p_1^{c_1}*p_2^{c_2}*cdots *p_k^{c_k}$。

根据题意,我们可以打表出质数表和逆元表。

线段树维护一个$tag$,用来压位$sum$的质因子。需要的时候直接位运算统计答案即可。

时间复杂度$O(q(60+log n))$。

PS:考试的时候我怎么这么傻逼,没写逆元模数直接GG。数据结构也写的一坨屎……

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=19961993;
int prime[65]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281};
int inv[65]={0,9980997,6653998,11977196,8555140,5444180,1535538,10568114,14708837,3471651,11701858,17386252,1618540,16066970,2321162,18263100,16948862,12518538,15380552,10725847,1686929,13399146,17182475,12025297,15924736,13582387,395287,6395590,15857658,16299242,6359573,3300802,18742940,6702567,10914471,16210746,11765678,5340151,18247466,7769638,8077107,11932588,6506948,1985748,6619521,5877135,4413707,9744480,10115270,14597757,16475182,18334191,5011379,18885205,7555336,621385,11309266,12170137,12006660,18304499,11153142};
int q;
struct node
{
    int index,l,r,sum,tag;
}tree[500005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void pushup(int index)
{
    tree[index].sum=tree[index*2].sum*tree[index*2+1].sum%mod;
    tree[index].tag=tree[index*2].tag|tree[index*2+1].tag; 
}
inline void build(int index,int l,int r)
{
    tree[index].l=l;
    tree[index].r=r;
    if (l==r)
    {
        tree[index].sum=3;
        tree[index].tag=2;
        return;
    }
    int mid=(l+r)>>1;
    build(index*2,l,mid);
    build(index*2+1,mid+1,r);
    pushup(index);
}
inline void update(int index,int pos,int x)
{
    if (tree[index].l==tree[index].r)
    {
        tree[index].sum=x;
        tree[index].tag=0;
        for (int i=1;i<=60;i++)
            if (!(x%prime[i])) tree[index].tag|=1ll<<(i-1);
        return;
    }
    int mid=(tree[index].l+tree[index].r)>>1;
    if (pos<=mid) update(index*2,pos,x);
    else update(index*2+1,pos,x);
    pushup(index);
}
inline pair<int,int> query(int index,int l,int r)
{
    if (l<=tree[index].l&&tree[index].r<=r) return make_pair(tree[index].sum,tree[index].tag);
    int mid=(tree[index].l+tree[index].r)>>1;pair<int,int> res;res.first=1;res.second=0;
    if (l<=mid)
    {
        pair<int,int> sum=query(index*2,l,r);
        res.first=res.first*sum.first%mod;
        res.second|=sum.second;
    }
    if (r>mid)
    {
        pair<int,int> sum=query(index*2+1,l,r);
        res.first=res.first*sum.first%mod;
        res.second|=sum.second;
    }
    return res;
}
inline int calc(int x,int tag)
{
    int ans=x;
    for (int i=1;i<=60;i++) if (tag&(1ll<<(i-1))) ans=ans*inv[i]%mod*(prime[i]-1)%mod;
    return ans;
} 
signed main()
{
    q=read();
    build(1,1,100000);
    for (int i=1;i<=q;i++)
    {
        int opt=read(),l=read(),r=read();
        if (opt==1) update(1,l,r);
        else{
            pair<int,int> ans=query(1,l,r);
            printf("%lld
",calc(ans.first,ans.second));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13363226.html