noip11.4分解数 twopointers+欧拉筛

Description

Dpstr学习了动态规划的技巧以后,对数的分解问题十分感兴趣。
Dpstr用此过程将一个正整数x分解成若干个数的乘积:一开始令集合A中只有一个元素x,每次分解时从A中取一个元素a并找出两个大于1且互质的整数p,q,要求pq=a,然后将a分解成两个元素p和q,也就是从A中删去a并加入p和q。Dpstr把正整数x用该过程能分解的次数的最大值称为x的分解数。
例如66的分解数为2,因为最多分解2次。一种分解过程为:一开始A={66},第1次将66分解为11×6,此时A={11,6},第2次将6分解为2×3,此时A={11,2,3},之后无法分解。还可以知道,11,2,3的分解数均为0,因为它们一开始就无法分解。
不过只分解一个数对Dpstr来说不够有趣。Dpstr生成了一个包含n个正整数的数列a1, a2, …, an,请你回答有多少对正整数(l,r)满足1≤l≤r≤n且lcm(al, al+1, …, ar-1, ar)的分解数恰为k。其中lcm(al, al+1, …, ar-1, ar)表示数列从第l项到第r项的所有数的最小公倍数,特别地,当l=r时,lcm(al)=al。由于答案可能很大,只需输出满足条件的正整数对个数除以10,007的余数。

Input

输入文件名为dec.in。
第一行包含两个正整数n,k,意义如题目所示。
第二行包含n个用空格隔开的正整数,分别表示a1, a2, …, an。

Output

输出文件名为dec.out。
输出一行一个整数,表示满足条件的正整数对个数除以10,007的余数。

Sample Input

样例输入1:
3 1
2 3 6
样例输入2:
10 2
2 3 2 6 15 5 5 5 2 3
样例输入3:
见\10.0.3.47\noip2016\11.4\user\dec\dec3.in

Sample Output

样例输出1:
4
样例输出2:
29
样例输出3:
见\10.0.3.47\noip2016\11.4\user\dec\dec3.ans

Data Constraint

对于20%的数据,1≤n≤10,1≤k≤5,1≤ai≤20;
对于40%的数据,1≤n≤100,1≤k≤10,1≤ai≤100;
对于60%的数据,1≤n≤1,000,1≤k≤1,000,1≤ai≤100,000;
对于100%的数据,1≤n≤1,000,000,1≤k≤5,000,000,1≤ai≤10,000,000。

Hint

考虑所有满足1≤l≤r≤4的正整数对(l,r):
1、对于(1,1),lcm(2)=2,分解数为0;
2、对于(2,2),lcm(3)=3,分解数为0;
3、对于(3,3),lcm(6)=6,分解数为1;
4、对于(1,2),lcm(2,3)=6,分解数为1;
5、对于(2,3),lcm(3,6)=6,分解数为1;
6、对于(1,3),lcm(2,3,6)=6,分解数为1。
其中,6的分解数为1是因为{6}可以分解为{2,3},之后无法分解。
因此共有3对正整数(l,r)满足条件。

题解:这题有毒。
比赛的时候想到了NLOGN的打法然而没有时间了,全部时间都花在第三题超级暴力大模拟身上。。。。
具体做法是,枚举左端点,然后右端点一直向右拓展,每次对于元素的出队入队都要更新当前维护的lcm的分解数,问题是怎么维护呢?
首先要知道lcm是单调递增的,而且分解数其实就是区间内所有只是出现过一次的质因数,所以我们要开个数组来记录当前区间内每个质因子出现的质数(1e7以内的质数数量为66万左右),然后怎么知道每一个新加入的数的质因子呢?直接根号暴力枚举肯定不行,所以,我们先跑一边欧拉筛,然而直接暴力枚举质数表还是会爆,随意我们要维护个东西last[x],表示x的最大因数,这样每次维护的时候就可以log时间维护了,表示脑洞真大。

const mo=10007;
var
        i,j,k,n,m,s,d,l,r:longint;
        p:Array[0..10000000]of boolean;
        b,last:array[0..10000000]of longint;
        a,pr:Array[0..1000000]of longint;
procedure ins(x:longint);
var
        h:longint;
begin
        h:=0;
        while x>1 do
        begin
                if (h<>x div last[x])then
                begin
                        inc(b[x div last[x]]);
                        if(b[x div last[x]]=1)then
                        begin
                                inc(d);
                        end;
                end;

                h:=x div last[x];
                x:=last[x];
        end;
end;
procedure del(x:longint);
var
        h:longint;
begin
        h:=0;
        while x>1 do
        begin
                if (h<>x div last[x])then
                begin
                        dec( b[x div last[x]]);
                        if (b[x div last[x]]=0)then
                        begin
                                dec(d);
                        end;
                end;
                h:=x div last[x];
                x:=last[x];
        end;
end;
function solve(k:longint):int64;
var
        ans,l,r:int64;
begin
        ans:=0;
        l:=1;
        r:=0;
        while l<=n do
        begin
                while (r<n)and(d<=k) do
                begin
                        inc(r);
                        ins(a[r]);
                end;
                ans:=ans+r-l;
                if d<=k then
                ans:=ans+1;
                del(a[l]);
                inc(l);
        end;
        exit(ans);
end;
begin
        {assign(input,'dec.in');
        assign(output,'dec.out');
        reset(input);
        rewrite(output);}
        read(n,k);
        inc(k);
        for i:=2 to 10000000 do
        begin
                if not p[i] then
                begin
                        inc(s);
                        pr[s]:=i;
                        last[i]:=1;
                end;
                for j:=1 to s do
                begin
                        if i*pr[j]>10000000 then break;
                        p[i*pr[j]]:=true;
                        last[i*pr[j]]:=i;
                        if i mod pr[j]=0 then break;
                end;
        end;
        for i:=1 to n do read(a[i]);
        //for i:=1 to 100 do writeln(last[i]);
        writeln((solve(k)-solve(k-1))mod mo);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值