【BZOJ】【3930】【CQOI2015】选数

数论/莫比乌斯反演/快速mu前缀和


  比较容易想到令f[x]表示gcd=x的方案数,令g[x]表示x|gcd的方案数。

  那么有$ g(d)=\sum_{d|n} f(n)$,根据莫比乌斯反演,有$f(d)=\sum_{d|n} g(n)*\mu (\frac{n}{d})$

  我一开始想的是算出g以后,倒序枚举 i ,然后枚举 i 的倍数,递推出所有的f[i]……

  因为g比较好算嘛……快速幂一下什么的……

  然而$10^9$直接吓傻我。

  Orz PoPoQQQ

  快速求出mu的前缀和,$10^9$也照样不虚,太神辣

 1 /**************************************************************
 2     Problem: 3930
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:4200 ms
 7     Memory:54024 kb
 8 ****************************************************************/
 9  
10 //BZOJ 3930
11 #include<cstdio>
12 #include<map>
13 #include<cstring>
14 #include<cstdlib>
15 #include<iostream>
16 #include<algorithm>
17 #define rep(i,n) for(int i=0;i<n;++i)
18 #define F(i,j,n) for(int i=j;i<=n;++i)
19 #define D(i,j,n) for(int i=j;i>=n;--i)
20 #define pb push_back
21 using namespace std;
22 typedef long long LL;
23 inline int getint(){
24     int r=1,v=0; char ch=getchar();
25     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
26     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
27     return r*v;
28 }
29 const int N=1e7+1000,P=1e9+7;
30 const int INF=0x3f3f3f3f;
31 /*******************template********************/
32  
33 int mu[N],prime[1001001],tot;
34 bool check[N];
35 map<int,LL> mu_sum;
36 LL n,d,l,r;
37 void getmu(){
38     int n=10000000;
39     mu[1]=1;
40     F(i,2,n){
41         if (!check[i]){
42             mu[i]=-1;
43             prime[++tot]=i;
44         }
45         F(j,1,tot){
46             int k=i*prime[j];
47             if (k>n) break;
48             check[k]=1;
49             if (i%prime[j]) mu[k]=-mu[i];
50             else{
51                 mu[k]=0;
52                 break;
53             }
54         }
55     }
56     F(i,1,n) mu[i]+=mu[i-1];
57 }
58 LL Mu_sum(int x){
59     if (x<=10000000) return mu[x];
60     if (mu_sum.find(x)!=mu_sum.end())
61         return mu_sum[x];
62     LL i,last,re=1;
63     for(i=1;i<=x;i=last+1){
64         last=x/(x/i);
65         if (x/i-1)
66             re-=(Mu_sum(last)-Mu_sum(i-1))*(x/i-1);
67     }
68     return mu_sum[x]=re;
69 }
70 LL Pow(LL a,int b){
71     LL r=1;
72     for(;b;b>>=1,a=a*a%P) if (b&1) r=r*a%P;
73     return r;
74 }
75 LL solve(){
76     LL i,last,re=0;
77     for(i=1;i<=r;i=last+1){
78         last=min(r/(r/i),l/i?(l/(l/i)):INF);
79         re+=(Mu_sum(last)-Mu_sum(i-1))*Pow(r/i-l/i,n);
80         re%=P;
81     }
82     return (re%P+P)%P;
83 }
84 int main(){
85 #ifndef ONLINE_JUDGE
86     freopen("3930.in","r",stdin);
87     freopen("3930.out","w",stdout);
88 #endif
89     n=getint(); d=getint(); l=getint(); r=getint();
90     l=(l-1)/d; r=r/d;
91     getmu();
92     printf("%lld\n",solve());
93     return 0;
94 }
View Code

  然而$H-L \leq 10^5$并没用?不是的……我们可以枚举倍数!

  Orz syk

  记f[i]为gcd恰好为$K*i$的选数方案数,那么对于每一个$i$记$L$为$\lceil \frac{a}{K*i}\rceil $,$R$为$\lfloor\frac{b}{K*i}\rfloor$ 那么他的方案数就为$f[i] = (R-L+1) ^ N - (R-L+1)-\sum_{a=1,2,\dots} f[a*i]$最后的f[1]即为答案。注意若$\lceil \frac{a}{K} \rceil == 1$ 那么全部选K也是一种方案,需要+1。

  (话说好不容易想到一次莫比乌斯反演,然而却做不出来……唉

 1 /**************************************************************
 2     Problem: 3930
 3     User: Tunix
 4     Language: C++
 5     Result: Accepted
 6     Time:412 ms
 7     Memory:1664 kb
 8 ****************************************************************/
 9  
10 //BZOJ 3930
11 #include<cstdio>
12 #include<cstring>
13 #include<cstdlib>
14 #include<iostream>
15 #include<algorithm>
16 #define rep(i,n) for(int i=0;i<n;++i)
17 #define F(i,j,n) for(int i=j;i<=n;++i)
18 #define D(i,j,n) for(int i=j;i>=n;--i)
19 #define pb push_back
20 using namespace std;
21 typedef long long LL;
22 inline int getint(){
23     int r=1,v=0; char ch=getchar();
24     for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
25     for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
26     return r*v;
27 }
28 const int N=1e5+10,P=1e9+7;
29 /*******************template********************/
30  
31 int d[N],n,K,l,r,a,b;
32 int Pow(int a,int b){
33     int r=1;
34     for(;b;b>>=1,a=(LL)a*a%P) if (b&1) r=(LL)r*a%P;
35     return r;
36 }
37 int main(){
38 #ifndef ONLINE_JUDGE
39     freopen("3930.in","r",stdin);
40     freopen("3930.out","w",stdout);
41 #endif
42     n=getint(); K=getint(); a=getint(); b=getint();
43     l=a/K; r=b/K;
44     if (a%K) l++;
45     D(i,100000,1){
46         int R=r/i,L=l/i;
47         if (l%i) L++;
48         if (l<=r){
49             d[i]=Pow(R-L+1,n);
50             d[i]=(d[i]-(R-L+1)+P)%P;
51             for(int j=i+i;j<=100000;j+=i) d[i]=(d[i]-d[j]+P)%P;
52         }
53     }
54     if (l==1) d[1]=(d[1]+1)%P;
55     printf("%d\n",d[1]);
56     return 0;
57 }
View Code

3930: [CQOI2015]选数

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 434  Solved: 222
[Submit][Status][Discuss]

Description

 我 们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方 案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要 回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。

Input

输入一行,包含4个空格分开的正整数,依次为N,K,L和H。

Output

输出一个整数,为所求方案数。

Sample Input

2 2 2 4

Sample Output

3

HINT

 样例解释


所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)

其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)

对于100%的数据,1≤N,K≤10^9,1≤L≤H≤10^9,H-L≤10^5

Source

[Submit][Status][Discuss]
posted @ 2015-06-16 21:49  Tunix  阅读(...)  评论(...编辑  收藏