zoukankan      html  css  js  c++  java
  • 莫比乌斯反演优化版

     正常的莫比乌斯反演写出后,对应某些题目实践尽然还会被卡,证明还有优化得余地,面对这个反演函数我们能在哪做文章呢??

    求和?NO,莫比函数?NO,F(d)may be?

    F(d)是我们最不费力的一个,特就是b/i,d/i(b,d是区间上限),但注意这是相除后去余的,所以你模拟一下,看一看 b      i       b / i        b / (b / i)

    5      1         5               1

    5       2        2                2

    5      3         1                5

    5      4         1               5

    5      5         1               5

    看看b / i项明显的就是重复了,如果把求和展开的化,我们都会有一个结合率的冲动把,但是这个结合的前提是,你得准备好mobi函数的前缀和,类似于分块的公共乘积优化

    for(int i = 2;i < maxn;i++)
        {
            if(!mark[i]){pri[++tot] = i;mb[i] = -1;}
            for(int j = 1;j <= tot;j++)
            {
                int x = pri[j];
                if(x * i >= maxn)break;
                mark[i * x] = 1;
                if(i % x == 0){mb[i * x] = 0;break;}
                else mb[i * x] = -mb[i];
            }
            s[i] = s[i - 1] + mb[i];
        }
    

    其实也就是在正常线性筛莫比乌斯反演的时候,用s数组记录一下前缀和

    如何用这个结合呢,看看b / (b / i) 吧,这个会直接跳到最后一个能结合的下标,+1就是新的索引了~~ for(int i = 1;i <= a;i = last + 1)     {         last = min(a / (a / i),b / (b / i));         ret += (a / i) * (b / i) * (s[last] - s[i - 1]);     }

    last就是代表的跳的位置,结合中i保留的是上一个last所以相减即可

    #include <iostream>
    #include <string.h>
    #include <cstdio>
    using namespace std;
    const int maxn = 5e4 + 5e2;
    int mb[maxn];
    int tot = 0;
    int mark[maxn],s[maxn],pri[maxn];
    void getmb()
    {
        memset(mark,0,sizeof(mark));
        mb[1] = 1;
        s[1] = 1;
        for(int i = 2;i < maxn;i++)
        {
            if(!mark[i]){pri[++tot] = i;mb[i] = -1;}
            for(int j = 1;j <= tot;j++)
            {
                int x = pri[j];
                if(x * i >= maxn)break;
                mark[i * x] = 1;
                if(i % x == 0){mb[i * x] = 0;break;}
                else mb[i * x] = -mb[i];
            }
            s[i] = s[i - 1] + mb[i];
        }
    
    
    }
    int getret(int a,int b)
    {
        if(a > b)swap(a,b);
        int ret = 0,last;
        for(int i = 1;i <= a;i = last + 1)
        {
            last = min(a / (a / i),b / (b / i));
            ret += (a / i) * (b / i) * (s[last] - s[i - 1]);
        }
        return ret;
    }
    int main()
    {
        int t,a,b,c,d,k;
        scanf("%d",&t);
        getmb();
        while(t--)
        {
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
            int ret = getret(b/k,d/k) - getret((a-1)/k,d/k) - getret(b/k,(c-1)/k) + getret((a-1)/k,(c-1)/k);
            printf("%d
    ",ret);
        }
        return 0;
    }
    
  • 相关阅读:
    数据库中的大数据字段和二进制大数据字段(图片)
    mysql中的存储过程和事务隔离
    关于数据库的三个范式的详解
    mysql与java的之间的连接
    mysql中的第三范式
    如何设置ssh安全只允许用户从指定的IP登陆
    MySQL 出现 The table is full 的解决方法
    ssh "openssh-daemon is stopped"操作之伤+sftp访问“-bash: /dev/null: Permission denied”
    CentOS 6 用SVN自动提交文件到web服务器
    centos下pg_dump的服务器版本不匹配问题
  • 原文地址:https://www.cnblogs.com/DF-yimeng/p/8490487.html
Copyright © 2011-2022 走看看