您的位置:首页 > 其它

hdu 5514 Frog(容斥原理)

2016-01-06 23:02 375 查看
题目链接:hdu 5514

题目大意:N只青蛙,M个石头构成一个环,第i只青蛙每一次向后跳Ai个石头,即下一步的位置是(当前位置+Ai)
mod m,最开始的时候所有青蛙都在位置0,每只青蛙会跳无数次,问最终所有被青蛙跳过的石头的编号之和是多少?

解题思路:第i只青蛙会踩过的石头编号分别为0,gcd(Ai, m), 2 * gcd(Ai, m) ……一共有m/gcd(Ai,m)-1个,这个的和很是容易用等差数列求和计算出来……但是不同的i跳过的石头有重复!
去重的方法是容斥原理(莫比乌斯我不还不会。。),求出m的所有因数,记录每个gcd(Ai,m)的贡献,一边求和一边除去多算的贡献,用数组c[]记录多算的贡献,v[]记录贡献,详见代码。

ps.1~10^9的所有数中,约数最多的数有1344个约数,所以O(n^2)不会超时。

</pre><pre class="cpp" name="code">#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<string.h>
#define ll long long
using namespace std;
vector<int>ft;
int v[2005],c[2005],ca=0, a;
void qft(int m)
{
ft.clear();
int n=(int)sqrt(m)+1;
for(int i=1; i<n; i++)
{
if(!(m%i))
{
ft.push_back(i);
if(m/i!=i)ft.push_back(m/i);
}
}
}
int gcd(int a,int b)
{
while(a&&b)
{
if(a>b)a=a%b;
else b=b%a;
}
return a+b;
}
int find(int m)
{
return lower_bound(ft.begin(),ft.end(),m)-ft.begin();
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
memset(v,0,sizeof(v));
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
qft(m);
sort(ft.begin(),ft.end());
for(int i=0; i<n; i++)
{
scanf("%d",&a);
int d=gcd(a,m);
v[find(d)]=1;//c语言函数不能嵌套!否则会tle
}
int len=ft.size();
for(int i=0; i<len; i++)
{
if(v[i])
for(int j=i+1; j<len; j++)
if(!v[j]&&ft[j]%ft[i]==0)v[j]=1;
}
ll ans=0;
for(int i=0; i<len; i++)
{
ll x=v[i]-c[i];
if(x==0)continue;
ans+=(ll)(m/ft[i]-1)*(m/ft[i])/2*x*ft[i];//小心越界啊大兄弟
for(int j=i; j<len; j++)
if(ft[j]%ft[i]==0)c[j]+=x;
}
printf("Case #%d: %I64d\n",++ca,ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: