HDU 6069 数论 区间素数筛 + 赛后反思

题目链接


x = p 1 a 1 p 2 a 2 . . . . p n a n x = p_1^{a_1}p_2^{a_2}....p_n^{a_n} x=p1a1p2a2....pnan
d ( x ) = ( 1 + a 1 ) ( 1 + a 2 ) . . . ( 1 + a n ) d(x) = (1+a_1)(1+a_2)...(1+a_n) d(x)=(1+a1)(1+a2)...(1+an)
d ( x k ) = ( 1 + k a 1 ) ( 1 + k a 2 ) ( 1 + k a 3 ) . . . ( 1 + k a n ) d(x^k) = (1+ka_1)(1+ka_2)(1+ka_3)...(1+ka_n) d(xk)=(1+ka1)(1+ka2)(1+ka3)...(1+kan)

k k k已知,所以我们只需要知道 L &lt; = x &lt; = R L&lt;=x&lt;=R L<=x<=R的每一个x质因数分解后的 a 1 , a 2 . . . a n a_1,a_2...a_n a1,a2...an便可以求解出答案。

x &lt; = 1 e 12 x&lt;=1e12 x<=1e12,故可以考虑枚举出 1 e 6 1e6 1e6以内的素数,采用区间素数筛求出每一个数的质因数指数。(可参考白书(《挑战程序设计竞赛》)素数一章的例题)

设区间长度为 n n n
总复杂度 O ( R + n l o g l o g n ) O(\sqrt R+nloglog n) O(R +nloglogn)


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int P = 998244353;
const int G = 3;
const int A = 1e6 + 10;
const int N =  70;
bool vis[A];
int pri[A],tot;

void init(){
    tot = 0;
    for(ll i=2 ;i<A ;i++){
        if(!vis[i]){pri[++tot] = i;}
        for(ll j=1 ;j<=tot && i*pri[j]<A ;j++){
            vis[i*pri[j]] = 1;
            if(i%pri[j] == 0) break;
        }
    }
}

ll ans[A],val[A];
void solve(ll L,ll R,ll t){
    for(ll i=L ;i<=R ;i++){
        ans[i-L] = 1;
        val[i-L] = i;
    }
    for(ll i=1 ;i<=tot ;i++){
        ll now = pri[i];
        for(ll j=max(2LL,(L+now-1)/now)*now ;j<=R ;j+=now){
            if(val[j-L] % now) continue;
            ll cnt = 0;
            while(val[j-L] % now == 0){val[j-L]/=now;cnt++;}
            ans[j-L] = (1+t*cnt)%P*ans[j-L]%P;
        }
    }
    for(ll i=L ;i<=R ;i++){
        if(val[i-L] > 1){
            ans[i-L] = (1+t)%P*ans[i-L]%P;
        }
    }
    ll sum = 0;
    for(ll i=L ;i<=R ;i++){
        sum = (sum + ans[i-L]) % P;
    }
    printf("%I64d\n",sum);
}

int main(){
    init();
    int T;
    scanf("%d",&T);
    while(T--){
        ll l,r,k;
        scanf("%I64d%I64d%I64d",&l,&r,&k);
        solve(l,r,k);
    }
    return 0;
}

反思:非常简单的一题。本来我队开场三十分钟就推出了该公式并在白书上找到了区间筛的原题和代码。按正常剧情本应该是最多一小时内就能正确AC此题,但作为队内此题的负责人,却终场也没有搞出来qwq

问题主要出在两个方面。
一是对自己的第一直觉太自信,赛前两天才开始学FFT和NTT,一看到取模的质数是一个费马质数,存在原根,虽然NTT学得并不好,但仍然固执地认定此题一定需要用到NTT进行优化,并带偏了队友的思路,在没有跟队友讨论确定思路(因对需要使用NTT太自信,而两位队友都没学过该算法,故错误地认为没有讨论的必要)的情况下直接开写,用NTT将 d ( i k ) = ( 1 + k a 1 ) ( 1 + k a 2 ) ( 1 + k a 3 ) . . . ( 1 + k a n ) d(i^k) = (1+ka_1)(1+ka_2)(1+ka_3)...(1+ka_n) d(ik)=(1+ka1)(1+ka2)(1+ka3)...(1+kan)

关于k的多项式进行展开运算,从而引起时间复杂度爆炸,提交的代码T了很多次,浪费了大量时间。

另外一方面便在于对算法时间复杂度的估算不够重视。本来半小时想出的思路是可以正确AC的,却凭自我感觉认为其复杂度太高(因结果表达式太直接地就推了出来),跳过了复杂度估算的步骤,一直错误地思考着优化式子的办法,并强行用上了NTT(果然还是太弱了qwq

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值