数论-沈阳站-hdu-5514-容斥

题目意思是:有n只青蛙,m个石头0到m-1,每只青蛙初始在0号石头上面,每只青蛙能跳a[i]步,问所有被至少一只青蛙跳到的石头的下表总和。

解题思路:首先对于每只青蛙他可以跳到的石头应该是gcd(a[i],m)的倍数的石头,但是如果分开来求肯定会有重复,所以我们采用容斥原理。所以我们先找出所有m的约数,然后再将每只青蛙能跳到的约数标记为1.用vis数组,因为有重复,所以我们用一个num数组来记录这个约数已经被贡献了几次。刚开始num全为0表示都没贡献过。开始模拟时,从小的约数开始,每个约数还能进行贡献的值为

vis[i]-num[i]在从1到k所以是k*(k-1) 其中k是m除以当前约数累加上这个值后,就要对num[j](p[j]%p[i]==0) 的贡献度进行加上。

PS:num[i]可能比vis[i]大

vis[i] p[i]需要的贡献度

num[i] p[i]当前的贡献度实时更新

#include
using namespace std;
long long  a[11111];
long long pp[22222];
long long vis[22222];
long long num[22222];
int main()
{
    long long  t;
    long long  n,m;
    long long  kase=0;
    scanf("%lld",&t);
    while(t--) {
        memset(vis,0,sizeof vis);
        memset(num,0,sizeof num);
        scanf("%lld%lld",&n,&m);
        long long nn=0;
        for(long long i=1;i<=sqrt(m);i++) {
            if(m%i==0) {
                pp[++nn]=i;
                if(i*i!=m) {
                    pp[++nn]=m/i;
                }
            }
        }
        sort(pp+1,pp+nn+1);
        for(long long i=1;i<=n;i++) {
            scanf("%lld",&a[i]);
            long long  tt=__gcd(a[i],m);
            for(int j=1;j<=nn;j++) {
                if(pp[j]%tt==0) {
                    vis[j]=1;
                }
            }
        }
        vis[nn]=0;
        long long ans=0;
        for(long long  i=1;i<=nn;i++) {
            if(vis[i]!=num[i]) {
                long long  ss=m/pp[i];
                long long  sss=vis[i]-num[i];
                ans+=ss*(ss-1)/2*pp[i]*sss;
                for(long long  j=i;j<=nn;j++) {
                    if(pp[j]%pp[i]==0)
                    num[j]+=sss;
                }
            }
        }
        printf("Case #%lld: %lld\n",++kase,ans);
    }
    return 0;
}



你可能感兴趣的:(数论)