【題解】玲瓏杯河南專場17B

  容斥大法妙~其實網上不少的題解雖然給出了容斥係數,可是並無說明爲何是這個樣子的。在這裏解釋一下好了。c++

  考慮用容斥,實際上就是讓 \(ans = \sum_{T\subseteq S}^{\ }f_{T}*h_{T}\)。其中,\(f\) 爲容斥的係數,而 \(h\) 爲一個集合的‘貢獻’。這個貢獻的值每每對於集合當中的各個元素而言是獨立的。因爲這題中是要咱們求出全部的被操做了奇數次的燈的數量,因此有:spa

\(g_{x}=\sum_{i = 1}^{x}\binom{x}{i}*f_{i}=[x\&1]\)code

\(g_{x}\) 爲是原數列中 \(x\) 個數的倍數的數所對答案產生的貢獻blog

令\(f[0] = 0\),get

則\(g_{x}=\sum_{i = 0}^{x}\binom{x}{i}*f_{i}=[x\&1]\)it

那麼根據二項式反演,有class

\(f_x = \sum_{i = 0}^{x} g_i * \binom{x}{i}*(-1)^{x - i}\)gc

\(f_x = \sum_{i = 0}^{x}\binom{x}{i}*(-1)^{x - i}[x\&1]\)集合

根據\(f_x = \sum_{i = 0}^{x}\binom{x}{i}*(-1)^{x - i}[x\&1]\)di

對\(x\) 的奇偶性分類討論一下,再加上:

\(\binom{n}{1}+\binom{n}{3}+\binom{n}{5}...=2^{n - 1}\)

(這個式子就不用解釋了吧……)

而後就獲得了\(f_x\) 的表達式~

  下面這份代碼爲 \(n^{2}\) 求出容斥係數,但實際上能夠按照上文所說作到\(O(1)\)……

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
#define int long long
int n, m, ans, cnt, S[maxn];
int f[maxn], a[maxn], C[maxn][maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void Pre()
{
    for(int i = 0; i < 30; i ++) C[i][0] = 1;
    for(int i = 1; i < 30; i ++)
        for(int j = 1; j < 30; j ++)
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}

int Get(int x)
{
    int t = x & 1;
    for(int i = 1; i < x; i ++)
        t -= C[x][i] * f[i];
    return t;
}

int gcd(int a, int b)
{
    int c = 0;
    while(b) c = a % b, a = b, b = c;
    return a;
}

void dfs(int now)
{
    if(now == m + 1)
    {
        int lcm = 1;
        for(int i = 1; i <= cnt; i ++)
            lcm = lcm * S[i] / gcd(lcm, S[i]);
        ans += f[cnt] * (n / lcm);
        return;
    }
    S[++ cnt] = a[now]; dfs(now + 1);
    cnt --; dfs(now + 1);
}

signed main()
{
    int T = read(); f[0] = 0; f[1] = 1; Pre();
    for(int i = 2; i <= 20; i ++) f[i] = Get(i);
    for(int i = 1; i <= T; i ++)
    {
        n = read(), m = read(); ans = 0;
        for(int j = 1; j <= m; j ++) a[j] = read();
        dfs(1);
        printf("%lld\n", ans);
    }
    return 0;
}
相關文章
相關標籤/搜索