hdu5514(有技巧的容斥)

题意:有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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值