bzoj3930(莫比乌斯反演)

来源:互联网 发布:js 隐藏之后无法再显示 编辑:程序博客网 时间:2024/05/03 15:51

图by popoqqq   http://blog.csdn.net/popoqqq/article/details/44917831



这道题,反演的部分应该不是特别难,像我这样的初学者都可以搞清楚,但是这道题需要常见的那个分块优化,需要求mo【】的前缀和,但是数据范围是1e9,肯定是不能预处理出来,然后上面就已经给出了直接计算mo前缀和的方法,sqrt,但是直接计算时间复杂度又不行。。。


至于mo前缀和计算的证明的话,还是需要再啃一啃。

#include<cstdio>#include<cstring>#include<cmath>#include<map>#include<cstdlib>#include<algorithm>using namespace std;typedef long long ll;const int N=1e7;const int inf=0x3f3f3f3f;const int mod=1000000007;ll n,k,l,h;ll p[N],mo[N+10],s[N+10];bool b[N+10];map<ll,ll> mp;void init(){mo[1]=s[1]=1;for (int i=2;i<=N;i++){if (!b[i]){mo[i]=-1;p[++p[0]]=i;}for (int j=1;j<=p[0]&&p[j]*i<=N;j++){b[i*p[j]]=true;if (i%p[j]==0){mo[i*p[j]]=0;break;}else mo[i*p[j]]=-mo[i];}s[i]=s[i-1]+mo[i];}}ll power(ll a,ll b){ll ans=1;while (b){if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans;}ll sum(ll n){if (n<=N) return s[n];if (mp.find(n)!=mp.end()) return mp[n];ll ans=1;for (ll i=1,r;i<=n;i=r+1){r=n/(n/i);if (n/i-1)ans-=(sum(r)-sum(i-1))*(n/i-1);}return mp[n]=ans;}ll work(ll l,ll h){ll ans=0;for (int i=1,r;i<=h;i=r+1){r=min(l/i ? l/(l/i):inf,h/(h/i));ans+=(sum(r)-sum(i-1))*power(h/i-l/i,n);ans%=mod;}return (ans%mod+mod)%mod;}int main(){scanf("%lld%lld%lld%lld",&n,&k,&l,&h);init();printf("%lld",work((l-1)/k,h/k));return 0;}


总结:

1:这道题引出一个非常重要的思想,预处理一部分,直接计算一部分,其中很多时候,一个大的数的函数值,是由比他小的数推出来的,并且后面不影响前面的,那么我们其实就可以进行记忆化搜索,搜到小的范围时直接返回预处理的答案,大的范围就直接计算,计算过的值用一个map存起来。

这样的思想在codevs角谷猜想中也用到了,常常配合着记忆化搜索。

2:这道题,求在l~r的区间内选n个数,gcd==d 的方案数,实际上,这一类的gcd==d,我们都可以尝试转化成gcd==1的问题,其实对于这道题就是,l/d~r/d中gcd==1的方案数,转化成这样后进行下底函数分块思路就清楚多了。很多求gcd==d的都是可以这么做



0 0
原创粉丝点击