hdu5514(有技巧的容斥)

来源:互联网 发布:锯棕榈 知乎 编辑:程序博客网 时间:2024/05/17 21:27

题意:有m个石子围成一圈, 有n只青蛙从跳石子, 都从0号石子开始, 每只能越过xi个石子。问所有被至少踩过一次的石子的序号之和。

思路:

首先对于每个青蛙而言,它能到达的位置是它每次能跳的长度x与m的GCD的倍数

但是对于很多的GCD的时候。可能会多算,,比如2的倍数,3的倍数,此时6的倍数就被多算了。。
其实也就是上边的那个思想用容斥的办法,既然6多算了一次,剪掉一次6的倍数就好。。
GCD(x,m)=p,p肯定是m的约数。我们首先考虑所有m的约数,,如果是青蛙能跳的倍数的就标记为1,表示这个约数要做出贡献值。。如果约束q做出了贡献,那么q的倍数的贡献也就要标记上。。
比如计算2的时候6被标记了一次,计算3的时候,6被也标记了一次,计算到6的时候,本应爱是visit==1但是我有num==2,标记计算了两次,所以要剪掉。visit-num,num也有可能是多次反复跟新约数的贡献值的次数。。


PS:这个容斥做得好巧妙!



Code:


#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e4 + 10;ll n,m;int fac[maxn];int vis[maxn];int num[maxn];int main(){    int T;    scanf("%d",&T);    for(int ii = 1; ii <= T; ii ++)    {        memset(vis,0,sizeof(vis));        memset(num,0,sizeof(num));       scanf("%I64d%I64d",&n,&m);       int tot = 0;       for(int i = 1; i * i <= m;i ++)       {           if(m % i == 0)           {               fac[tot ++] = i;               if(i * i != m)                fac[tot ++] = m/i;           }       }       sort(fac,fac + tot);       tot --;       for(int i = 1; i <= n;i ++)       {           ll x;           scanf("%I64d",&x);           ll d = __gcd(x,m);           for(int j = 0; j < tot; j ++)            if(fac[j] % d == 0)            vis[j] = 1;       }       ll ans = 0;       for(int i = 0; i < tot; i ++)       {           if(vis[i] != num[i])           {               ll t = (m - 1)/fac[i];               ans += t * (t + 1) / 2 * fac[i] * (vis[i] - num[i]);               for(int j = i + 1; j < tot; j ++)               {                   if(fac[j] % fac[i] == 0)                    num[j] += vis[i] - num[i];               }           }       }       printf("Case #%d: %I64d\n",ii,ans);    }    return 0;}



原创粉丝点击