hdu 5514 Frogs 容斥或欧拉函数

题目链接

题意 输入n,m,n代表青蛙个数 m代表石头个数 石头围成一圈 由0编号到m-1 第二行输入n只青蛙的步长,一块石头不能被两只青蛙同时占领,求石头编号的和,以第一组样例

2 12

9 10 为例

2 即两只青蛙 12是石头个数  两只青蛙最开始都在编号为0的石头上 第一只青蛙 可以跳到

 编号9 

(9+9)%12 编号6  

(18+9)%12 编号3  

(27+9)%12 编号0  

跳回原处 所以第一只青蛙是 0 3 6 9

第二只青蛙 同上述过程是 0 2 4 6 8 10

出现了相同的6 这时候就需要容斥来把 重复的去掉

易得青蛙的步长为gcd(a[i],m)

因为是gcd(a[i],m)所以一定是m的因子 把m的因子先预处理出来

然后x(m/x*(m/x-1))/2求出来每个因子能被占领的标号的和

通过两个数组一个标记是否访问一个标记计算次数 【大佬的巧妙容斥

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int t[maxn];
int num[maxn],v[maxn]; //被计算的次数 是否被访问
int main()
{
    int T;
    cin>>T;
    int n, m;
    int d=1;
    while(T--)
    {
        cin>>n>>m;
    memset(num,0,sizeof(num));
    memset(v,0,sizeof(v));
        int cnt=0;
        for(int i=1;i*i<=m;i++)  //m的因子
        {
            if(m%i==0)
            {
                   t[cnt++]=i;
                   if(i*i<m)
                    t[cnt++]=m/i;
            }
        }
        sort(t,t+cnt);
        int tt,h;
         for(int i=0;i<n;i++)
         {
             cin>>tt;
            tt=gcd(tt,m);
             for(int j=0;j<cnt;j++) //这个因子都可以被跳到  //去掉m
                if(t[j]%tt==0)
                    v[j]=1;
         }
         v[cnt-1]=0;
         ll ans=0;
         for(int i=0;i<cnt;i++)
         {
             if(v[i]!=num[i])  //学习大佬的容斥
             {
                 int f=(m-1)/t[i];
                 ans+=((ll)f*(f+1)/2*t[i]*(v[i]-num[i]));
                 int c=v[i]-num[i];
                 for(int j=i;j<cnt;j++) //修改当前因子的倍数的的计算次数
                     if(t[j]%t[i]==0)
                           num[j]+=c;
             }
         }
          printf("Case #%d: %lld\n",d++,ans);
    }
    return 0;
}
还有一种方法是欧拉函数的做法也是超级巧妙 

可以看看这个博客->点击打开链接
AC代码

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
long long ol(long long a)
{
    long long res,i,t=0;
     res=a;
    for(i=2;i*i<=a;i++)
    {
        if(a%i==0)
        {
            res=res-res/i;
            while(a%i==0)
            {
                a=a/i;
            }
        }
    }
    if(a>1)
    {
        res=res-res/a;
    }
    return res;
}
int t[maxn];
int v[maxn],num[maxn]; //被计算的次数 是否被访问
int main()
{
    int T;
    cin>>T;
    int n, m;
    int d=1;
    while(T--)
    {
        cin>>n>>m;
        int cnt=0;
        for(int i=1;i*i<=m;i++)  //m的因子
        {
            if(m%i==0)
            {
                   t[cnt++]=i;
                   if(i*i<m)
                    t[cnt++]=m/i;
            }
        }
        sort(t,t+cnt);
        memset(v,0,sizeof(v));
        int tt,h;
        int f=0;
         for(int i=0;i<n;i++)
         {
             cin>>tt;
          tt=gcd(tt,m);
           for(int j=0;j<cnt;j++) //这个因子都可以被跳到  //去掉m
                if(t[j]%tt==0)
                    v[j]=1;
         }
         v[cnt-1]=0;
         ll ans=0;
         for(int i=0;i<cnt;i++)
         {
               if(v[i]==1)
               {
                   int f=m/t[i];
                   ans+=t[i]*ol(f)*f/2;
               }
         }
          printf("Case #%d: %lld\n",d++,ans);
    }
    return 0;
}




猜你喜欢

转载自blog.csdn.net/yangdelu855/article/details/79064523