bzoj 5245: [Fjwc2018]欧拉函数 线段树+bitset

15 篇文章 0 订阅
0 篇文章 0 订阅

题意

对于正整数 n,定义欧拉函数 φ(n) 为小于等于 n 且与 n 互质的正整数个数。例如
φ(1) = 1, φ(8) = 4。
给定正整数序列 a1, a2, · · · , an,请依次执行 q 个操作,操作有以下三种类型:
0 i x:修改 ai 的值为 x;
1 l r:查询 φ(al + al+1 + · · · + ar) 的值,输出这个值对 10^9 + 7 取模的结果;
2 l r:查询 φ(al × al+1 × · · · × ar) 的值,输出这个值对 10^9 + 7 取模的结果。

1 ≤ i ≤ n, x ≥ 1, 1 ≤ l ≤ r ≤ n。
n ≤ 50000, q ≤ 100000,Ai及x<=40000操作 0 的个数不超过 20000,所有的 ai、
操作 0 中的 i, x 及操作 1,2 中的 l, r 均在给定的限制下内均匀随机生成

分析

首先对于第1问,考虑到加起来的结果最大也就是2e9,我们可以根号时间复杂来算出phi,又考虑数据随机,用线段树或者树状数组维护和,然后一个单次询问是

O(logN+sum) O ( l o g N + s u m )

考虑第二问,发现乘起来的话就不能根号时间算phi了,但是我们发现每次乘起来质因子是不会有变化的,而每个只要考虑出现了哪些质因子,每种质因子x对所有乘积的贡献是

x1x x − 1 x

然后怎么算出现了哪些质因子呢,线段树每个结点开一个bitset,下标为位置,然后预处理出每个数的bitset,修改就对应改掉,询问就把所有的bitset或起来
还有一个操作就是找bitset的每个1可以用这样来找

for(ll i=ans._Find_first();i<ans.size();i=ans._Find_next(i))

质因子个数发现只有4203个,于是就有时间复杂度

O(logN420332?) O ( l o g N 4203 32 ∗ ? )

其中这里的?是1的个数,我们要遍历一遍
考虑 数据随机,我们就可以完成这道题

代码


#include <bits/stdc++.h>
#define ll long long

using namespace std;

const ll N = 200010;
const ll mod = 1e9+7;

inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}


ll prime[N],pri,inv[N],idx[N]; bool v[N];
bitset<4210> bo[N],ans,pb[N];

void init(ll n)
{
  memset(v,1,sizeof(v)); v[0] = v[1] = 0; pri = 0;
  for(ll i=2;i<=n;i++)
  {
    if(v[i]) prime[++pri] = i,idx[i] = pri;
    for(ll j=1;(j<=pri) && (i*prime[j]<=n);j++)
    {
      v[i*prime[j]]=0;
      if(i%prime[j]==0) break;
    }
  }

  inv[0] = inv[1] = 1; for(ll i=2;i<=n;i++) inv[i] = (mod - mod / i) * inv[mod%i] % mod;

  for(ll i=1;i<=n;i++)
  {
    ll x=i;
    for(ll j=1;prime[j]*prime[j]<=x;j++)
    {
      while(x%prime[j]==0){pb[i].set(j); x/=prime[j];}
    }if(x!=1) pb[i].set(idx[x]);
  }
}

ll lc[N],rc[N],tot,rt,c[N],s[N];

void link(ll &u,ll L,ll R,ll k,ll cc)
{
  if(!u) u=++tot;
  if(L==R){bo[u] = pb[cc]; c[u] = s[u] = cc; return ;}
  ll mid=(L+R)>>1;
  if(k<=mid) link(lc[u],L,mid,k,cc);
  else link(rc[u],mid+1,R,k,cc);
  c[u] = c[lc[u]] + c[rc[u]];
  s[u] = (s[lc[u]] * s[rc[u]]) % mod;
  bo[u] = bo[lc[u]] | bo[rc[u]];
}

ll qry1(ll u,ll L,ll R,ll l,ll r)
{
  if(L==l && R==r) return c[u];
  ll mid=(L+R)>>1;
  if(r<=mid) return qry1(lc[u],L,mid,l,r);
  else if(l>mid) return qry1(rc[u],mid+1,R,l,r);
  else return qry1(lc[u],L,mid,l,mid) + qry1(rc[u],mid+1,R,mid+1,r);
}

ll qry2(ll u,ll L,ll R,ll l,ll r)
{
  if(L==l && R==r){ans |= bo[u]; return s[u];}
  ll mid=(L+R)>>1;
  if(r<=mid) return qry2(lc[u],L,mid,l,r);
  else if(l>mid) return qry2(rc[u],mid+1,R,l,r);
  else return qry2(lc[u],L,mid,l,mid) * qry2(rc[u],mid+1,R,mid+1,r) % mod;
}

ll qpow(ll x,ll k,ll mo){ll ss=1; while(k){if(k&1) ss=ss*x%mo; x=x*x%mo; k>>=1;} return ss;}

int main()
{

  init(40000);
  ll n = read(); ll m = read();
  rt=tot=0; for(ll i=1;i<=n;i++) link(rt,1,n,i,read());
  while(m--)
  {
    ll op = read();
    if(op==0){ll k = read(); link(rt,1,n,k,read());}
    else if(op==1)
    {
      ll l=read(); ll r=read(); ll sum = qry1(rt,1,n,l,r);
      ll x = sum;
      for(ll i=1;prime[i] * prime[i] <= x;i++)
      {
        if(x%prime[i]==0)
        {
          sum = sum * inv[prime[i]] % mod * (prime[i] - 1) % mod;
          while(x%prime[i]==0) x/=prime[i];
        }
      }
      if(x!=1) sum = sum * qpow(x,mod-2,mod) % mod * (x-1) % mod;
      printf("%lld\n",sum);
    }
    else
    {
      ll l=read(); ll r=read(); ans.reset();
      ll sum = qry2(rt,1,n,l,r);
      for(ll i=ans._Find_first();i<ans.size();i=ans._Find_next(i))
        sum = sum * inv[prime[i]] % mod * (prime[i]-1) % mod;
      printf("%lld\n",sum);
    }
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值