【做题】hdu5514 Frogs——另类容斥

题意是给出n个m的约数,问[0,m-1]中至少被其中一个约数整除的整数和。(n<=10000,m<=1000000000)

直接容斥的话,是2^n再拖个log的复杂度,加上当前的数大于m时直接跳出了剪枝,或许会小一点。

但是有一个很重要的性质:我们容斥中所有计算过贡献的数,都是m的因数。换言之,我们计算贡献的数的个数是及其有限的,故可以计算出所有因数贡献的系数。

我们给所有因数赋予互不重叠的“贡献”,定义为所有能被其整除但不能被其他大于它的因数整除的数的和。这种贡献是我们用来表示答案的组成的,但是不可以直接计算。

于是一开始,我们把所有是这n个约数的倍数的贡献设为1。事实上,容斥就体现在这里。我们这里是设为1,而不是加1。即对于一个同时是多个因数的倍数的数,我们只计算一次贡献。

然而,我们在实际计算中,一个数的贡献在它的所有因数中都计算过了。设因数i的实际系数是ri,初始定义的系数是vi,那么,我们有

时间复杂度O(√m+n*d(m)+d(m)^2)。(好像非常玄学)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 10010;
 4 typedef long long ll;
 5 ll ans;
 6 int a[N],n,m,p,fac[N],num[N],cnt,tmp;
 7 int main() {
 8   int t;
 9   scanf("%d",&t);
10   for (int ca = 1 ; ca <= t ; ++ ca) {
11     memset(num,0,sizeof num);
12     scanf("%d%d",&n,&m);
13     cnt = 0;
14     ans = 0;
15     for (int i = 1 ; i * i <= m ; ++ i) if (m%i == 0) {
16       fac[++ cnt] = i;
17       if (i * i != m) fac[++ cnt] = m/i;
18     }
19     sort(fac+1,fac+cnt+1);
20     for (int i = 1 ; i <= n ; ++ i) {
21       scanf("%d",&a[i]);
22       a[i] = __gcd(a[i],m);
23       for (int j = 1 ; j <= cnt ; ++ j)
24         if (fac[j] % a[i] == 0) num[j] = 1;
25     }
26     for (int i = 1 ; i <= cnt ; ++ i) if (num[i]) {
27       tmp = m / fac[i];
28       ans += 1ll * tmp * (tmp - 1) / 2 * fac[i] * num[i];
29       for (int j = i + 1 ; j <= cnt ; ++ j)
30         if (fac[j] % fac[i] == 0) num[j] -= num[i];
31     }
32     printf("Case #%d: %lld
",ca,ans);
33   }
34   return 0;
35 }

小结:在容斥对象数量少的时候,存下其中的每一个贡献的系数是可取的优化方向。

原文地址:https://www.cnblogs.com/cly-none/p/8440394.html