您的位置:首页 > 其它

HDU 4135&HDU 4407--容斥原理--质因子分解

2013-09-08 20:09 399 查看

HDU4135        Co-prime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 1032    Accepted Submission(s): 375


[align=left]Problem Description[/align]
Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.

Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1. The number 1 is relatively prime to every integer.
 

[align=left]Input[/align]
The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 1015) and (1 <=N <= 109).
 

[align=left]Output[/align]
For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.
 

[align=left]Sample Input[/align]

2
1 10 2
3 15 5

 

[align=left]Sample Output[/align]

Case #1: 5
Case #2: 10

HintIn the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.

 

[align=left]Source[/align]
The Third Lebanese Collegiate Programming Contest
 

[align=left]Recommend[/align]
lcy

我们首先来看HDU的4135题,作为HDU4407的入门。。。。。。

题意很简单,给定一个闭区间,一个数,查询闭区间内总共有多少个数与该数互质。

由于互质较为难以判断,我们不妨考虑互质的另一面,即不互质的个数num,

则ans=区间内的数字总数N-num。

那么怎么求出num呢,

我们先假设该数为n,求出n的全部质因子(任何一个数都能分解成一个或多个素数的乘积——算术基本定理。)

PS:同时可以发现,1.任何一个数最多只能有一个大于sqrt(n)的素数因子!!!!!(这个是我们等等计算一个数n的全部质因子的关键)
                                     2.如果n是一个合数,那么n必然有小于或等于sqrt(n)的一个素因子。
                                     3.如果n是一个素数,那么它不能被小于或等于其平方根的素数整除。

以下是求出n的全部质因子的模板(已优化)

int prime_yinzi[400000];//用来存储一个数的全部质因子。
int size;//prime_yinzi数组的大小

void find_prime_yinzi(int n)
{
int m=(int)(sqrt((double)n)+0.5);//四舍五入
size=0;
if(n%2==0)
{
prime_yinzi[size++]=2;
while(n%2==0) n/=2;
}//类似于筛法求素数,使得每次能只加2
for(int i=3;i<=m;i+=2) //这里求的都是,p^a中,a可能大于1的p。质因子从2开始算
{
if(n%i==0)
{
prime_yinzi[size++]=i;
while(n%i==0) n/=i;
}
if(n==1) return;
}
if(n>1) prime_yinzi[size++]=n;//因为也可能有大于根号N的质因子,易证明,此时p^a中,a最多为1.
}


那么知道了n的全部质因子有什么用呢?

任意一个数,它如果是n的质因子的倍数,那么它必然与n不互质!即二者gcd不为1!

即问题又转化为了区间内有多少个数是n的质因子的倍数。

由于一个不是从1开始的区间难以计算,我们可以转化一下,

(a,b)区间即为,用(1,b)区间内的个数减去(1,a-1)区间内的个数。

我们来模拟一下过程:

假设m=12,n=30,

第一步:求出n的质因子:2,3,5;

第二步:(1,m)中是n的因子的倍数当然就不互质了(2,4,6,8,10)->n/2  6个,(3,6,9,12)->n/3  4个,(5,10)->n/5  2个。

如果是粗心的同学就把它们全部加起来就是:6+4+2=12个了,那你就大错特错了,里面明显出现了重复的,我们现在要处理的就是如何去掉那些重复的了!

第三步:这里就需要用到容斥原理了,公式就是:n/2+n/3+n/5-n/(2*3)-n/(2*5)-n/(3*5)+n/(2*3*5)!

容斥原理的实现有三种方法:

队列数组,dfs,位运算。

= =#我只会后两种方法。。。。。

以下是HDU4135的代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;

int yinzi[10000];
int size;
ll ans;
void find_prime_yinzi(int n)
{
int m=(int)(sqrt((double)n)+0.5);//四舍五入
size=0;
for(int i=2;i<=m;++i) //这里求的都是,p^a中,a可能大于1的p。
{
if(n%i==0)
{
yinzi[size++]=i;
while(n%i==0) n/=i;
}
}
if(n>1) yinzi[size++]=n; //因为也可能有大于根号N的质因子,易证明,此时p^a中,a最多为1.
}
void dfs(ll n,int deep,int num,ll lcm)
{
if(deep>size)
{
if(num==0) return;
if(num&1) ans+=n/lcm;
else ans-=n/lcm;
return;
}
dfs(n,deep+1,num,lcm);
dfs(n,deep+1,num+1,lcm*yinzi[deep-1]);//因为全部为质因子,所以不需要再去搞一个最大公倍数,最大公倍数即为两个数的乘积。
}

int main()
{
//freopen("input.txt","r",stdin);
int T,cases=1;
scanf("%d",&T);
while(T--)
{
ll a,b,n;
//cin>>a>>b>>n;
scanf("%I64d%I64d%d",&a,&b,&n);
find_prime_yinzi(n);
ans=0;dfs(b,1,0,1);ll temp1=ans;
ans=0;dfs(a-1,1,0,1);ll temp2=ans;
ans=(b-temp1)-(a-1-temp2);
printf("Case #%d: %I64d\n",cases++,ans);
}
return 0;
}


以下是稍微难一点的HDU4407,


Sum

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 1456    Accepted Submission(s): 417


Problem Description

XXX is puzzled with the question below: 

1, 2, 3, ..., n (1<=n<=400000) are placed in a line. There are m (1<=m<=1000) operations of two kinds.

Operation 1: among the x-th number to the y-th number (inclusive), get the sum of the numbers which are co-prime with p( 1 <=p <= 400000).

Operation 2: change the x-th number to c( 1 <=c <= 400000).

For each operation, XXX will spend a lot of time to treat it. So he wants to ask you to help him.

 

Input

There are several test cases.

The first line in the input is an integer indicating the number of test cases.

For each case, the first line begins with two integers --- the above mentioned n and m.

Each the following m lines contains an operation.

Operation 1 is in this format: "1 x y p". 

Operation 2 is in this format: "2 x c".

 

Output

For each operation 1, output a single integer in one line representing the result.

 

Sample Input

1
3 3
2 2 3
1 1 3 4
1 2 3 6

 

Sample Output

7
0

 

Source

2012 ACM/ICPC Asia Regional Jinhua Online

 

Recommend

zhoujiaqi2010

注意条件a[ i ]=i的初始条件,

其实与HDU4135的想法相同,

唯一不同的是要用map对修改操作进行记录。

以下是代码:

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
int prime_yinzi[400000];//用来存储一个数的全部质因子。
int size;//prime_yinzi数组的大小
ll ans;
map<int,int> mp;

int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}

void find_prime_yinzi(int n)
{
int m=(int)(sqrt((double)n)+0.5);//四舍五入
size=0;
if(n%2==0)
{
prime_yinzi[size++]=2;
while(n%2==0) n/=2;
}
for(int i=3;i<=m;i+=2) //这里求的都是,p^a中,a可能大于1的p。质因子从2开始算
{
if(n%i==0)
{
prime_yinzi[size++]=i;
while(n%i==0) n/=i;
}
if(n==1) return;
}
if(n>1) prime_yinzi[size++]=n;//因为也可能有大于根号N的质因子,易证明,此时p^a中,a最多为1.
}

ll get_sum(int n)
{
return (ll)n*(n+1)/2;
}

void dfs(int x,int deep,int num,ll lcm)
{
if(deep>size)
{
if(num==0) return;
if(num&1) ans+=lcm*get_sum(x/lcm);
else if((num&1)==0) ans-=lcm*get_sum(x/lcm);
}
else
{
dfs(x,deep+1,num,lcm);
dfs(x,deep+1,num+1,lcm*prime_yinzi[deep-1]);
}
}

int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
while(m--)
{
int op;
scanf("%d",&op);
if(op==1)
{
int x,y,p;
scanf("%d%d%d",&x,&y,&p);
if(x>y) swap(x,y);
find_prime_yinzi(p);
ans=0;dfs(x-1,1,0,1);ll temp1=ans;
ans=0;dfs(y,1,0,1);ll temp2=ans;
ans=(get_sum(y)-temp2)-(get_sum(x-1)-temp1);
map<int,int>::iterator it1;
for(it1=mp.begin();it1!=mp.end();++it1)
{
int temp1=it1->first,temp2=it1->second;
if(temp1<x||temp1>y) continue;
if(gcd(temp1,p)==1) ans-=temp1;
if(gcd(temp2,p)==1) ans+=temp2;
}
cout<<ans;
printf("\n");
}
else if(op==2)
{
int x,c;
scanf("%d%d",&x,&c);
mp[x]=c;
}
}
mp.clear();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息