天天看點

Frogs HDU - 5514 (容斥原理,)題意:思路:

題意:

有一堆青蛙,一開始都在0點,然後有一堆圈成一圈的石子,石子的編号是從0-m-1的

然後青蛙隻能順時針跳,每個青蛙可以一次跳a[i]格,然後所有青蛙都這樣一直跳下去

然後問你,這些青蛙踩過的石子的編号和是多少?

思路:

首先,對于第i隻青蛙,他跳過的格子,一定是k*gcd(a[i],m)這種的

由于m 較大,是以我們找m的因子來做,m的因子一定也屬于 gcd(a[i],m);

m 的因子也有可能存在倍數關系,是以我們需要用到容斥原理。

我們先把m 的因子找出來,然後從小到大排序,如果m的因子是 最大公約數的倍數,那麼我們在這個因子的下标上

打上一個标記。說明這個因子可以用到。

我麼在設另外一個數組,num ,表示這個因子用了幾次,如果大于一次,那麼說明多用了,我們就需要減去這個因子所

産生的結果。

一開始,,所有數都沒有用到過,是以 num 應該都是 0;

我們用了一個因子之後,如果後面的有用的因子是目前因子的倍數,那麼後面的因子的num 就要加上 1 ;

if(vis[i] == num[i]) 說明在計算之前因子的時候,把這個因子計算過了,不需要用了。目前因子是之前因子的倍數,如果在用這個因子,會計算重複。

if (vis[i] != num[i]) 說明這個因子有可能被通路了,有可能沒有被通路。要看這兩個數的內插補點。

Frogs HDU - 5514 (容斥原理,)題意:思路:

比如說,m 是 24, 我們有 2 4 6 8 這幾個有用的因子,

我們先做2

之後我們就标記了 4, 6 ,8  因為做2 的時候就把 4 6 8 做完了,之後就不需要重複做了,

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define mem(x,v) memset(x,v,sizeof(x))
using namespace std;
const int N = 10005;
typedef long long LL;
int vis[N],num[N];
int p[N];
int n,m,k;
int gcd(int a, int b){
	if (b == 0) return a; else return gcd(b, a % b);
}

LL init(){
	mem(vis,0);
	mem(num,0);
	scanf("%d%d",&n,&m);
	k = 0;
	for (int i = 1; i * i <= m; i++){ //篩因子
		if (m % i == 0) {
			p[k++] = i;
			if (i * i != m) p[k++] = m / i;
		}
	}
	sort(p,p+k);
	for (int i = 0; i < n; i++){
		int x;
		scanf("%d",&x);
		x = gcd(x,m);
		for (int j = 0; j < k; j++)
			if (p[j] % x == 0) vis[j] = 1; // 找有用的因子,指派為1;
	}
	LL ans = 0;
	for (int i = 0; i < k; i++){
		if (vis[i] != num[i]){ //看看目前的因子有沒有被用過。
			int t = (m - 1) / p[i];
			ans += (LL)(t+1)*t/2*p[i]*(vis[i]-num[i]);
			t = vis[i] - num[i];
			for (int j = i; j < k; j++)
				if (p[j] % p[i] == 0) //找是目前因子的倍數,把次數加1,防止重複運算。
				num[j] += t;
		}
	}
    return ans;

}
int main(){
	int T,tt = 1;
	cin>>T;
	while(T--){
		LL ans = init();
		printf("Case #%d: %I64d\n",tt++,ans);
	}
	return 0;
}
           

繼續閱讀