您的位置:首页 > 其它

HDU 4407 Sum <容斥原理>

2016-10-03 22:38 344 查看
题目链接:Click here~~

第一道容斥原理的题目。

题意:

有一个元素为 1~n 的数列{An},有2种操作(1000次):

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

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

解题思路:

对于操作1,解的性质满足区间减法,则我们只需要考虑如何求 [1,n] 中与 p 互质的数的和即可。

考虑到与 p 互质的数不太好解,于是可以通过先求出与 p 不互质的数的和,然后与总和作差得到。

而一个数 x 若与 p 不互质,当且仅当两者素因子的集合有交集。

设 p 的素因子是{P1,P2,…,Pk},于是与 p 不互质的数的素因子集合可以表示成 {P1} U {P2} U … U {Pk}。

那么与 p 不互质的数的集合可以表示成 W = { P1的倍数 } U { P2的倍数 } U … U { Pk的倍数 }。

其中,{ Pk的倍数 } = { Pk*1 } + { Pk*2 } + … + { Pk*Mk } ( Pk*Mk<=n && Pk*(Mk+1)>n )。

从而,ans = sum{ W }。

于是可以通过容斥原理,求得问题的解。

举个简单的例子,假如 k=2,则 ans = 【sum{ P1的倍数 } + sum{ P2的倍数 } - sum{ P1*P2的倍数 }】。其中,某个sum{ }可以通过等差公式求得。

对于操作2,由于操作比较少,我们可以保存这些操作,当遇到要求和的时候,我们遍历之前保存的操作,找到在区间中改变的值,对应修改即可。

#include <cstdio>
#include <map>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn = 10010;
int Prime[maxn],vis_prime[maxn],factor[maxn];
int _fac;
void init_prime()
{
int _top = -1;
memset(vis_prime,0,sizeof(vis_prime));
for(int i=2;i<maxn;i++)
{
if(!vis_prime[i])
{
Prime[++_top] = i;
for(int j = i;j<maxn;j+=i)
vis_prime[j] = 1;
}
}
}
void set_factor(int p)
{
_fac = 0;
for(int i=0;Prime[i]*Prime[i]<=p;i++)
{
if(p%Prime[i])
continue;
while(p%Prime[i]==0)
{
p/=Prime[i];
}
factor[_fac++] = Prime[i];
}
if(p!=1)
factor[_fac++] = p;
// printf("_fac = %d\n",_fac);
}
LL ans;
LL pre_sum(int n)
{
return (LL)(n+1)*n/2;
}
void dfs(int step,int tot,int i,int fac,int num,int n)
{
//printf("")
//printf("step = %d tot = %d i = %d fac = %d num = %d n = %d\n",step,tot,i,fac,num,n);
if(tot == i)
{
LL temp = num * pre_sum(n/num);
i&1? ans -= temp: ans += temp;
return ;
}
if(fac-step < i-tot||num*factor[step]>n)
return ;
dfs(step+1,tot+1,i,fac,num*factor[step],n );
dfs(step+1,tot,i,fac,num,n);
}
LL solve(int n)
{
ans = pre_sum(n);
for(int i=1;i<=_fac;i++)
{
dfs(0,0,i,_fac,1,n);
}
return ans;
}
int gcd(int a,int b)
{
return b ? gcd(b,a%b) : a;
}
int main()
{
int cas;
int n,m;
int op,x,y,p,c;
map<int,int> mp;
init_prime();
scanf("%d",&cas);
while(cas--)
{
mp.clear();
scanf("%d %d",&n,&m);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d %d %d",&x,&y,&p);
set_factor(p);
LL sum = solve(y) - solve(x-1);
//printf("m == %d pre_sum = %lld p = %d\n",m,sum,p);
for(map<int,int>::iterator it = mp.begin();it!=mp.end();it++)
{
//cout << it->first << " " <<it->second << endl;
if((*it).first>=x&&(*it).first<=y)
{
if(gcd((*it).first,p)==1)
sum -= (*it).first;
if(gcd((*it).second,p)==1)
sum += (*it).second;
}

}

printf("%I64d\n",sum);
}
else
{
scanf("%d %d",&x,&c);
mp[x] = c;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: