您的位置:首页 > 其它

hdu 5514 Frogs 容斥或欧拉函数

2018-01-15 15:01 363 查看
题目链接

题意 输入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)

4000
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: