数论 | 容斥原理

∣ S 1 ∪ S 2 ∪ S 3 ∣ = ∣ S 1 ∣ + ∣ S 2 ∣ + ∣ S 3 ∣ − ∣ S 1 ∩ S 2 ∣ − ∣ S 1 ∩ S 2 ∣ − ∣ S 1 ∩ S 3 ∣ − ∣ S 2 ∩ S 3 ∣ + ∣ S 1 ∩ S 2 ∩ S 3 ∣ |S_1 ∪ S_2 ∪ S_3| = |S_1|+|S_2|+|S_3|-|S_1∩S_2|-|S_1∩S_2|-|S_1∩S_3|-|S_2∩S_3|+|S_1∩S_2∩S_3| S1S2S3=S1+S2+S3S1S2S1S2S1S3S2S3+S1S2S3

时间复杂度: O ( 2 n ) O(2^n) O(2n)
C n 1 + C n 2 + C n 3 + . . . + C n n = 2 n − C n 0 = 2 n − 1 C_n^1+C_n^2+C_n^3+...+C_n^n=2^n-C_n^0=2^n-1 Cn1+Cn2+Cn3+...+Cnn=2nCn0=2n1

能被整除的数

暴力枚举的时间复杂度: O ( n m ) O(nm) O(nm)

容斥原理求解:

  • ∣ S i ∣ |S_i| Si,即 能被 i 整除的元素个数。
  • 问题就转化成了求 ∣ S i ∪ . . . ∪ S j ∣ |S_i∪...∪S_j| Si...Sj,可用容斥原理求解。
  • ∣ S p ∣ |S_p| Sp ⌊ n p ⌋ \lfloor \frac{n}{p} \rfloor pn
  • 时间复杂度: O ( 2 m × m ) O(2^m\times m) O(2m×m)
  • 可以用二进制数表示选法
#include 
using namespace std;

typedef long long LL;
int n, m;
const int N = 18;
int p[N];
LL res;

int main()
{
    cin >> n >> m;
    for(int i=0; i<m; i++) {
        cin >> p[i];
    }
    // 遍历所有状态
    for(int i=1; i<1<<m; i++) {
        LL t=1;  // 记录乘积
        int cnt=0;  // 统计1的个数 
        for(int j=0; j<m; j++) {
            if(i>>j & 1) {
                t *= p[j];
                cnt++;
                if(t > n) {
                    t = -1;
                    break;
                }
            }
        }
        if(t != -1) {
            if(cnt%2 == 1)  res += n/t;
            else  res -= n/t;
        }
    }
    cout << res << endl;
    return 0;
}

你可能感兴趣的:(算法,c++,数据结构,容斥原理,数论)