1. 程式人生 > >區間互素(篩法尤拉函式模板+容斥原理)(1695)

區間互素(篩法尤拉函式模板+容斥原理)(1695)

GCD

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8818    Accepted Submission(s): 3268


Problem Description Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.

Yoiu can assume that a = c = 1 in all test cases.


Input The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.

Output For each test case, print the number of choices. Use the format in the example.

Sample Input 2 1 3 1 5 1 1 11014 1 14409 9
Sample Output Case 1: 9 Case 2: 736427

原題題意是求[1,b]中的x和[1,d]中的y,滿足gcd(x,y)=k的個數。首先很重要的一步就是轉化成[1,b/k]中的x和[1,d/k]中的y,滿足gcd(x,y)=1的個數,所以原題轉化成了兩個區間互素二元組的個數。我們可以從大區間(假設[1,d])列舉i,求i在[1,b]區間內與其互素的數的個數,這樣的話可以分成兩種情況:①當i在區間[1,b]內時,直接尤拉函式得到值(為了提高效率尤拉函式的陣列記錄累加值,這樣可以直接得到euler[b]) ;②當超出區間時,這時就是一個經典的容斥求互素個數的問題了(在計算尤拉函式時就可以把每個數的因子儲存在一個數組中,這樣方便後面直接使用)。

/*------------------Header Files------------------*/
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <ctype.h>
#include <cmath>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <vector>
#include <set>
#include <limits.h>
using namespace std;
/*------------------Definitions-------------------*/
#define LL long long
#define uLL unsigned long long
#define PI acos(-1.0)
#define INF 0x3F3F3F3F
#define MOD 9973
#define MAX 100005
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
/*---------------------Work-----------------------*/
int a,b,c,d,k;
LL ans;
LL euler[MAX]; //不是單純的某個數的尤拉函式值,是從1到n的累加值
int num[MAX]; //儲存當前數的因子個數
int prime[MAX][10]; //儲存當前數的因子
void EulerPrime() //篩法的尤拉函式
{
	euler[1]=1; //1和自己互素
	for(int i=2;i<MAX;i++)
	{
		if(!euler[i])
		{
			for(int j=i;j<MAX;j+=i)
			{
				if(!euler[j]) euler[j]=j;
				euler[j]=euler[j]/i*(i-1); //先除後乘,防溢位
				prime[j][num[j]++]=i;
			}
		}
		euler[i]+=euler[i-1]; //不能少這一句,因為是累加值
	}
}
void DFS(int num1,int cur,int cnt,LL temp) //經典的DFS容斥函式
{
	temp*=prime[num1][cur];
	if(cnt&1) ans+=b/temp;
	else ans-=b/temp;
	for(int i=cur+1;i<num[num1];i++)
		DFS(num1,i,cnt+1,temp);
}
void work()
{
	int T;
	EulerPrime();
	/*
	for(int i=1;i<=20;i++)
	{
		printf("%d:euler:%d ",i,euler[i]);
		for(int j=0;j<num[i];j++)
			printf("%d ",prime[i][j]);
		printf("\n");
	}
	*/
	while(scanf("%d",&T)==1)
	{
		for(int Case=1;Case<=T;Case++)
		{
			printf("Case %d: ",Case);
			scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
			if(k==0) //k還可以為0
			{
				printf("0\n");
				continue;
			}
			if(d<b) swap(b,d);
			b/=k,d/=k;
			LL res=euler[b]; //列舉的i小於b時直接可以得到
			//當i大於b時就要使用容斥原理了
			for(int i=b+1;i<=d;i++)
			{
				ans=0;
				for(int j=0;j<num[i];j++)
					DFS(i,j,1,1);
				res+=b-ans;
			}
			printf("%I64d\n",res);
		}
	}
}
/*------------------Main Function------------------*/
int main()
{
	//freopen("test.txt","r",stdin);
	work();
	return 0;
}