bzoj 4026: dC Loves Number Theory 数论+主席树

Description
dC 在秒了BZOJ 上所有的数论题后,感觉萌萌哒,想出了这么一道水题,来拯救日益枯
竭的水题资源。
给定一个长度为 n的正整数序列A,有q次询问,每次询问一段区间内所有元素乘积的
φ(φ(n)代表1~n 中与n互质的数的个数) 。由于答案可能很大,所以请对答案 mod 10^6 +
777。 (本题强制在线,所有询问操作的l,r都需要 xor上一次询问的答案 lastans,初始时,
lastans = 0)

Input
第一行,两个正整数,N,Q,表示序列的长度和询问的个数。
第二行有N 个正整数,第i个表示Ai.
下面Q行,每行两个正整数,l r,表示询问[l ^ lastans,r ^ lastans]内所有元素乘积的φ

Output
Q行,对于每个询问输出一个整数。

Sample Input
5 10
3 7 10 10 5
3 4
42 44
241 242
14 9
1201 1201
0 6
245 245
7 7
6 1
1203 1203

Sample Output
40
240
12
1200
2
240
4
4
1200
4

HINT
1 <= N <= 50000
1 <= Q <= 100000
1 <= Ai <= 10^6

Source
orz Loidc

分析:
我们对所有数进行质因数分解。
a n s = ∏ i = l r a i ans=\prod_{i=l}^{r}a_i ans=i=lrai。对于一个质数 p p p,如果它出现过,那么就 a n s ∗ = p − 1 p ans*=\frac{p-1}{p} ans=pp1。这个应该很显然。
前面这个可以直接前缀积,考虑后面这些要怎么求。
考虑怎么用可持久化线段树求区间不同元素的个数。
我们设 f [ i ] f[i] f[i]表示有边界为 r r r时,左边界为 i i i的答案。当我们枚举到 r + 1 r+1 r+1时,假设 k k k a i + 1 a_{i+1} ai+1上一次出现的位置,那么 f [ k + 1 ] f[k+1] f[k+1] f [ i + 1 ] f[i+1] f[i+1]的答案都加了1。
所以我们要可持久化,把右边界为 1 1 1~ n n n都记录下来,考虑差分把区间修改单点查询变为单点修改区间查询即可。
对于本题,每个质数的贡献变为了 p − 1 p \frac{p-1}{p} pp1,而且是乘起来,那么我们可以在 k + 1 k+1 k+1处乘 p − 1 p \frac{p-1}{p} pp1,在 i + 2 i+2 i+2处乘 p p − 1 \frac{p}{p-1} p1p即可。
注意初值要为1,如果不一开始开一棵线段树的话要注意特判。

7777777

代码:

/**************************************************************
    Problem: 4026
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:5772 ms
    Memory:174752 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
 
const int maxn=5e4+7;
const int maxp=1e6+7;
const LL mod=1e6+777;
 
using namespace std;
 
int n,m,x,y,tot,sc,cnt;
int a[maxn],num[maxn],last[maxp],root[maxn*20];
int not_prime[maxn],prime[maxn];
LL prod[maxp],pa[maxn],pb[maxn],ans;
 
struct node{
    int l,r;
    LL data;
}t[maxn*200];
 
LL power(LL x,LL y)
{
    if (y==0) return 1;
    if (y==1) return x;
    LL c=power(x,y/2);
    c=(c*c)%mod;
    if (y&1) c=(c*x)%mod;
    return c;
}
 
void getprime(int n)
{
    for (int i=2;i<=n;i++)
    {
        if (!not_prime[i]) prime[++tot]=i;
        for (int j=1;j<=tot;j++)
        {
            if (i*prime[j]>n) break;
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
}
 
void ins(int &p,int q,int l,int r,int x,LL k)
{
    if (!p) p=++cnt;
    if (!t[q].data) t[p].data=k;
               else t[p].data=t[q].data*k%mod;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,k);
           else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,k);
}
 
LL getprd(int p,int l,int r,int x,int y)
{
    if ((l==x) && (r==y))
    {
        if (!t[p].data) return 1;
        return t[p].data;
    }
    int mid=(l+r)/2;
    if (y<=mid) return getprd(t[p].l,l,mid,x,y);
    else if (x>mid) return getprd(t[p].r,mid+1,r,x,y);
    else return getprd(t[p].l,l,mid,x,mid)*getprd(t[p].r,mid+1,r,mid+1,y)%mod;
}
 
int main()
{
    scanf("%d%d",&n,&m);
    prod[0]=1;
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        prod[i]=prod[i-1]*(LL)a[i]%mod;
    }
    getprime(1e3);
    for (int i=1;i<=tot;i++)
    {
        pa[i]=((LL)prime[i]-1)*power((LL)prime[i],mod-2)%mod;
        pb[i]=power(pa[i],mod-2);
    }   
     
    for (int i=1;i<=n;i++)
    {
        int x=a[i];
        for (int j=1;j<=tot;j++)
        {
            if (x%prime[j]==0)
            {
                ins(root[++sc],root[sc],1,n,last[prime[j]]+1,pa[j]);
                if (i<n) ins(root[++sc],root[sc],1,n,i+1,pb[j]);
                while (x%prime[j]==0) x/=prime[j];
                last[prime[j]]=i;
            }
        }
        if (x>1)
        {
            LL pA=((LL)x-1)*power((LL)x,mod-2)%mod;
            LL pB=power(pA,mod-2);
            ins(root[++sc],root[sc],1,n,last[x]+1,pA);
            if (i<n) ins(root[++sc],root[sc],1,n,i+1,pB);
            last[x]=i;
        }
        num[i]=sc;
    }   
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        x^=ans,y^=ans;      
        ans=prod[y]*power(prod[x-1],mod-2)%mod;             
        ans=ans*getprd(root[num[y]],1,n,1,x)%mod;
        printf("%lld\n",ans);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值