您的位置:首页 > 其它

hdu 4407 容斥定理

2013-02-06 02:02 309 查看
题意:有一个元素为 1~n 的数列{An},有2种操作(1000次):

1、求某段区间 [a,b] 中与 p 互质的数的和。

2、将数列中某个位置元素的值改变。

思路:acm数学方面实在很差~学习ing~看解题报告A的

容斥定理,看《组合数学》时看到过,但是从来没用过,求第x个数到第y个数中与p互素的数的和。

p=p1^a1 * p2^a2 * p3^a3 * ……其中p1,p2,p3……是n的素因子,ai是pi的指数。

求出 1~y的与p互素的数的和 减去 1~x的与p互素的数的和。

只要是pi的倍数的,则不可能与p互素。所以例如30=2*3*5;那么

1~y的与30互素的数的和=y*(y+1)-2的倍数和-3的倍数和-5的倍数和+2*3的倍数和+3*5的倍数和+2*5的倍数和-2*3*5的倍数和+(改变量)。

(改变量):如果改变的的大于y则无视,否则,如果 改变后的值 与p互素,则+改变后的值

                      如果 改变前的值 与p互素,则-改变前的值。

感觉在处理容斥定理这一部分的代码写的很好。

View Code

#include <map>
#include <string.h>
#include <stdio.h>
#include <iostream>
using namespace std;
const int N=400005;
int factor[30];//存因子的数组
bool isPrime
;//判断是否是素数表
int prime
,cnt;//存素数
map<int,int>M;//存第2种操作,改变的值
int gcd(int a,int b){//最大公约数
while(a=a%b)a^=b^=a^=b;
return b;
}
void IsPrime(){//筛选法打素数表
int i,j;
cnt=0;
memset(isPrime,1,sizeof(isPrime));
for(i=2;i<N;i++){
if(isPrime[i]){
for(j=i+i;j<N;j+=i)
isPrime[j]=0;
prime[cnt++]=i;
}
}
}
int getPrime(int p){//求p的所有素数因子
if(isPrime[p]) {factor[0]=p;return 1;}
int i,k=0;
for(i=0;i<cnt;i++){
if(p%prime[i]==0) factor[k++]=prime[i];
while(p%prime[i]==0) p/=prime[i];
if(p!=1&&isPrime[p]) {factor[k++]=p;return k;}
}
return k;
}
long long find(int x,int n,int p){//find()函数求在1~x内与p互素的个数
int i,j,t,d;//j是第几个因子的下角标,t是临时
int m=1<<n;//p有n个素因子
int num;//记录第几次,决定加还是减 例如,30=2*3*5,-2-3-5 +2*3+3*5+2*5 -2*3*5
long long ans=x*(x+1)/2;
for(i=1;i<m;i++){
t=i;j=num=0;d=1;
while(t){
if(t&1) {d*=factor[j];num++;}
j++;t>>=1;
}
t=x/d;
if(num&1) ans-=(long long)d*t*(t+1)/2;//必须加强制类型转换,否则会WA
else ans+=(long long)d*t*(t+1)/2;//必须加强制类型转换,否则会WA
}
map<int,int>::iterator it;
for(it=M.begin();it!=M.end();it++){
if(it->first>x) continue;
if(gcd(it->first,p)==1) ans-=it->first;
if(gcd(it->second,p)==1) ans+=it->second;
}
return ans;
}
int main(){
int i;
int t,n,m;
int flag,x,y,p;
IsPrime();
scanf("%d",&t);
while(t--){
M.clear();
scanf("%d%d",&n,&m);
for(i=0;i<m;i++){
scanf("%d",&flag);
if(flag==1){
scanf("%d%d%d",&x,&y,&p);
int num=getPrime(p);//p素因子个数num
printf("%I64d\n",find(y,num,p)-find(x-1,num,p));
}else{
scanf("%d%d",&x,&p);
M[x]=p;
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: