Codeforces 900D Unusual Sequences 容斥原理

题目链接:900D  Unusual Sequences

题意:

  给出两个数N,M。让你求数列(和为M,gcd为N)的个数。

题解:

  首先,比较容易发现的是M%N如果不为零,那么一定不能构成这样的序列。那么可以设 k = M/N,则可以想象为用k个1来构成序列的个数,运用隔板原理可以求出k个1可以构成的序列总数为2^(k-1),但是这里面其实有不构成条件的(gcd>N)比方说6个相同的数(2,2,2)构成这样gcd就是2×N而不是N了。所以要减去这些数的情况,这样减的话发现不能用递归来做,要先记忆化。记忆化因为这里面最大的数是1e9不能用数组储存,要用map离散。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_N=1e5+5;
 4 const int MOD = 1e9+7;
 5 map<long long , long long > mp;
 6 long long quick_pow(int x)
 7 {
 8     x -= 1;
 9     long long base = 2;
10     long long ans = 1;
11     while(x)
12     {
13         if(x&1) ans = (ans * base)%MOD;
14         x >>= 1;
15         base = (base*base)%MOD;
16     }
17     return ans;
18 }
19 long long solve(int x)
20 {
21     if(mp.count(x)) return mp[x];
22     long long ans = quick_pow(x)-1;
23     for(int i=2;i*i<=x;i++)
24     {
25         if(x%i == 0)
26         {
27             ans = (ans - solve(i) + MOD)%MOD;
28             if(x/i != i)
29             {
30                 ans = (ans - solve(x/i) + MOD)%MOD;
31             }
32         }
33     }
34     mp.insert(make_pair(x,ans));
35     return ans;
36 }
37 int main()
38 {
39     long long N,M,T;
40     mp.insert(make_pair(1,1));
41     while(cin>>N>>M)
42     {
43         if(M%N != 0)
44         {
45             printf("0\n");
46         }
47         else
48         {
49             printf("%lld\n",solve(M/N));
50         }
51     }
52     return 0;
53 }

 

posted @ 2018-01-23 11:32  会打架的程序员不是好客服  阅读(470)  评论(0编辑  收藏  举报