HDU 5514容斥定理

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5514

题意:有一堆青蛙,一开始都在0点,然后有一堆圈成一圈的石子,石子的编号是从0~m-1。

青蛙只能顺时针跳,第i只青蛙可以一次跳a[i]格,然后所有青蛙都这样一直跳下去然后问你,这些青蛙踩过的石子的编号和是多少?

题解:

首先,对于第i只青蛙,他跳过的格子,一定是k*gcd(a[i],m)这种的

如果m小一点,我们就可以直接暴力了

当时m太大了,我们就分解m的因数之后,对于每个因数做暴力就好了

每个因数T的贡献是 for(int i=1;i<=M/T;i++)ans += M*i;

然后优化一下就好了,对于部分加多了的因数,我们后面用容斥搞一搞就行了

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
using namespace std;
#define maxn 10005
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
//每个青蛙,可以跳到gcd(m,a[i])*k的位置
int ppp[maxn];
int num[maxn],vis[maxn];
int main()
{
    int tt;scanf("%d",&tt);
    for(int cas=1;cas<=tt;cas++)
    {
        int n,m;
        int cnt = 0;
        memset(vis,0,sizeof(vis));
        memset(num,0,sizeof(num));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=sqrt(m);i++)//把因子全部筛出来
        {
            if(m%i==0)
            {
                ppp[cnt++]=i;
                if(i*i!=m)
                    ppp[cnt++]=m/i;
            }
        }
        sort(ppp,ppp+cnt);
        for(int i=0;i<n;i++)
        {
            int x;scanf("%d",&x);
            int kk = gcd(x,m);
            for(int j=0;j<cnt;j++)
                if(ppp[j]%kk==0)//说明这个因子的所有,都是可以被跳到的位置
                    vis[j]=1;
        }
        vis[cnt-1]=0;//显然 m是不可能被跳到的
        long long ans = 0;
        for(int i = 0; i < cnt; i++)
        {
            if(vis[i] != num[i])
            {
                int t = (m-1)/ppp[i];
                ans += (long long)t*(t+1)/2 * ppp[i] * (vis[i]-num[i]);
                //容斥一波
                //一开始vis[i] - num[i] = 1的
                //对于每个因数,如果重复计算了,在之后,减去就好了
                t = vis[i] - num[i];
                for(int j = i; j < cnt; j++)
                    if(ppp[j]%ppp[i] == 0)
                        num[j] += t;
            }
        }
        printf("Case #%d: %lld\n",cas,ans);
    }
}

转自大佬:https://www.cnblogs.com/qscqesze/p/4933949.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值