HDU - 5514 Frogs(容斥原理)

题目链接:点击查看

题目大意:给出 0 ~ m - 1 共 m 个石头首尾相连呈现出一个环状,初始时在第 0 个石头上有 n 只青蛙,n 只青蛙相互独立,每一只青蛙每次都会向前跳 a[ i ] 步,问最后有多少个石头可以被跳过至少一次,输出石头的编号之和

题目分析:首先打个表不难看出,对于每只青蛙 i 来说,他可以到达的石头的编号是 gcd( a[ i ] , m ) 的所有倍数,又因为一个石头可能会被多个青蛙到达,所以考虑容斥

考虑直接容斥,我们现在需要解决的问题实际上是:0 ~ m-1 中有多少个数可以被这 n 个数整除,时间复杂度是 2^n 的,显然是不可以的

通过观察不难发现,每一只青蛙的 gcd 和所有起到贡献的 lcm 都是属于 m 的因子,其余那些大于 m 的 lcm 实际上是不做贡献的,所以我们就将问题转换为了对 m 的因子进行容斥,根据倍数限制 k * k 去维护答案就好了,k 是因子的个数

又因为 1e9 内最多的一个数的因子个数是 1536 个,所以这个题目的时间复杂度也只是 1e6 级别的

代码:
 

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;

const int N=1e4+100;

int a[N],num[N],n,m;

bool vis[N];

vector<int>p;

LL cal(LL st,LL n)//知道首项和项数求等差数列之和 
{
	return st*n+n*(n-1)/2*st;
}

void Fact()//筛因子
{
	p.clear();
	for(int i=1;i*i<=m;i++)
	{
		if(m%i==0)
		{
			p.push_back(i);
			if(i!=m/i)
			p.push_back(m/i);
		}
	}
	sort(p.begin(),p.end());
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int w;
	cin>>w;
	int kase=0;
	while(w--)
	{
		memset(num,0,sizeof(num));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",a+i);
			a[i]=__gcd(a[i],m);
		}
		Fact();
		for(int i=1;i<=n;i++)
			for(int j=0;j<p.size();j++)
				if(p[j]%a[i]==0)
					num[j]=1;
		LL ans=0;
		for(int i=0;i<p.size();i++)
		{
			ans+=num[i]*cal(p[i],(m-1)/p[i]);//当前i位置的因子会产生num[i]的贡献
			for(int j=i+1;j<p.size();j++)//其倍数就要减去num[i]的贡献
				if(p[j]%p[i]==0)
					num[j]-=num[i];
		}
		printf("Case #%d: %lld\n",++kase,ans);
	}












	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108531954