JZOJ 4638 第三条跑道 【NOIP2016提高组A组7.16】

第三条跑道

该题目的名字是一首歌

题目大意

这里写图片描述
这里写图片描述
这里写图片描述

输入格式

这里写图片描述

输出格式

对于每个询问,单独一行输出答案。

样例输入

5
2 3 4 5 6
3
1 1 5
0 2 3 6
1 2 3

样例输出

32
48

数据范围

这里写图片描述

题解

我们先看一下 φ 的通式。
这里写图片描述
其中p1, p2……pn为x的所有质因数,x是不为0的整数。
再看一下数据范围,这里写图片描述,这也就意味着 ai 在任何时刻都满足它的素因子是600以内的,而 φ(ai) 只跟它的素因子有关。

因为是区间查询/修改,当然是开线段树了。
600以内的素因子有109个,所以我们就种109棵线段树维护每一段区间的含有该素数的 ai 的个数。(如果 y |x,我们就说 x 含有y
在维护格式的同时,我们也维护这段区间 Πri=l φ(ai) ai 的欧拉函数的值的积)

我们把通式分成两部分, A B两个部分,如图所示
这里写图片描述

我们把修改时的 x 分解质因数以后一个一个质因数去更新区间。
当我们给某段区间乘上一个质因数y时(设该区间内有 u 个数,其中有ky个数含有质因数 y ),我们分类讨论。

对于那ky个含有质因数 y 的数,乘上一个y以后,在通式内, A 部分乘上了y,因为这 ky 个数含有 y ,所以B部分不变,所以这 ky 个数乘上了 y 以后对该区间欧拉积(Πri=l φ(ai) )的贡献就是乘上 yky

对于剩下的那 u -ky个不含有素数 y 的数,它们通式里的的A部分乘上了一个 y ,因为它们不含有素数y,所以 B 部分乘上了一个(1- 1y ),所以其实,它的欧拉函数的值乘上了 y *1- 1y ,化简后可得,它的欧拉函数的值乘上了( y1 ),所以这 u -ky个不含有素数 y 的数再乘上了y以后,对该区间欧拉积( Πri=l φ(ai) )的贡献就是乘上 (y1)uky

综上所述,该区间乘上了素数 y 以后,它的欧拉积就会乘上(y1)uky* yky ,并且乘上了素数 y 后,该区间的每一个素数都含有了素数y,所以要把 u <script type="math/tex" id="MathJax-Element-57">u</script>更新一下,就可以了。

懒惰标记下传,更新方法跟上面的一样,至于查询,答案就是对应的多段区间的欧拉函数积的积。

Code(Pascal)

const
    mo=100000007;
var
    bz:array[0..600] of boolean;
    a,ss,ny:array[0..30000] of int64;
    la:array[0..50000,0..109] of longint;
    tr:array[0..50000,0..109] of int64;
    j,k,l,i,oo,p,n,q,pd,x,y,z:longint;
    ans:int64;
function ksm(o,t:int64):int64;
    var
        cqy:int64;
    begin
        cqy:=1;
        while t>0 do
        begin
            if t mod 2=1 then cqy:=cqy*o mod mo;
            t:=t div 2;
            o:=o*o mod mo;
        end;
        exit(cqy);
    end;
procedure jl(o,l,r:longint);
    var
        i,mid:longint;
    begin
        if l=r then
        begin
            tr[o,0]:=a[l];
            for i:=1 to oo do
            if a[l] mod ss[i]=0 then
            begin
                inc(tr[o,i]);
                tr[o,0]:=tr[o,0]*(ss[i]-1) div ss[i];
            end;
            exit;
        end;
        mid:=(l+r) div 2;
        jl(o*2,l,mid);
        jl(o*2+1,mid+1,r);
        tr[o,0]:=tr[o*2,0]*tr[o*2+1,0] mod mo;
        for i:=1 to oo do
        tr[o,i]:=tr[o*2,i]+tr[o*2+1,i];
    end;
procedure gx(o,l,r,ll,rr,k:longint);
    var
        i,mid,ls,rs:longint;
    begin
        if (l=ll) and (r=rr) then
        begin
            la[o,k]:=la[o,k]+1;
            tr[o,0]:=(tr[o,0]*ksm(ss[k]-1,r-l+1-tr[o,k]) mod mo)*ksm(ss[k],tr[o,k]) mod mo;
            tr[o,k]:=r-l+1;
            exit;
        end;
        mid:=(l+r) div 2;
        ls:=o*2;
        rs:=ls+1;
        for i:=1 to oo do
        if la[o,i]>0 then
        begin
            tr[ls,0]:=(tr[ls,0]*ksm(ss[i]-1,mid-l+1-tr[ls,i]) mod mo)
                      *ksm(ss[i],tr[ls,i]) mod mo;
            tr[rs,0]:=(tr[rs,0]*ksm(ss[i]-1,r-mid-tr[rs,i]) mod mo)
                      *ksm(ss[i],tr[rs,i]) mod mo;
            tr[ls,0]:=tr[ls,0]*ksm(ss[i],(mid-l+1)*(la[o,i]-1)) mod mo;
            tr[rs,0]:=tr[rs,0]*ksm(ss[i],(r-mid)*(la[o,i]-1)) mod mo;
            tr[ls,i]:=mid-l+1;
            tr[rs,i]:=r-mid;
            la[ls,i]:=la[ls,i]+la[o,i];
            la[rs,i]:=la[rs,i]+la[o,i];
            la[o,i]:=0;
        end;
        if rr<=mid then gx(ls,l,mid,ll,rr,k)
        else if ll>mid then gx(rs,mid+1,r,ll,rr,k)
        else
        begin
            gx(ls,l,mid,ll,mid,k);
            gx(rs,mid+1,r,mid+1,rr,k);
        end;
        for i:=1 to oo do
        tr[o,i]:=tr[ls,i]+tr[rs,i];
        tr[o,0]:=tr[ls,0]*tr[rs,0] mod mo;
    end;
procedure cx(o,l,r,ll,rr:longint);
    var
        i,mid,ls,rs:longint;
    begin
        if (l=ll) and (r=rr) then
        begin
            ans:=ans*tr[o,0] mod mo;
            exit;
        end;
        mid:=(l+r) div 2;
        ls:=o*2;
        rs:=ls+1;
        for i:=1 to oo do
        if la[o,i]>0 then
        begin
            tr[ls,0]:=(tr[ls,0]*ksm(ss[i]-1,mid-l+1-tr[ls,i]) mod mo)
                      *ksm(ss[i],tr[ls,i]) mod mo;
            tr[rs,0]:=(tr[rs,0]*ksm(ss[i]-1,r-mid-tr[rs,i]) mod mo)
                      *ksm(ss[i],tr[rs,i]) mod mo;
            tr[ls,0]:=tr[ls,0]*ksm(ss[i],(mid-l+1)*(la[o,i]-1)) mod mo;
            tr[rs,0]:=tr[rs,0]*ksm(ss[i],(r-mid)*(la[o,i]-1)) mod mo;
            tr[ls,i]:=mid-l+1;
            tr[rs,i]:=r-mid;
            la[ls,i]:=la[ls,i]+la[o,i];
            la[rs,i]:=la[rs,i]+la[o,i];
            la[o,i]:=0;
        end;
        if rr<=mid then cx(ls,l,mid,ll,rr)
        else if ll>mid then cx(rs,mid+1,r,ll,rr)
        else
        begin
            cx(ls,l,mid,ll,mid);
            cx(rs,mid+1,r,mid+1,rr);
        end;
        for i:=1 to oo do
        tr[o,i]:=tr[ls,i]+tr[rs,i];
        tr[o,0]:=tr[ls,0]*tr[rs,0] mod mo;
    end;
begin
    readln(n);
    for i:=1 to n do
    read(a[i]);
    for i:=2 to 600 do
    if bz[i]=false then
    begin
         inc(oo);
         ss[oo]:=i;
         for l:=i to 600 div i do
         bz[i*l]:=true;
    end;
    jl(1,1,n);
    readln(q);
    for l:=1 to q do
    begin
        read(pd);
        if pd=1 then
        begin
            readln(x,y);
            ans:=1;
            cx(1,1,n,x,y);
            writeln(ans);
        end
        else
        begin
            readln(x,y,z);
            for i:=1 to oo do
            while z mod ss[i]=0 do
            begin
                gx(1,1,n,x,y,i);
                z:=z div ss[i];
            end;
        end;
    end;
end.
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值