您的位置:首页 > 其它

HDU 4407 Sum ★(容斥原理)

2012-11-29 20:14 369 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4407

题目大意:给定初始n个数1..n,两个操作,①1 x y p  询问第x个数到第y个数中与p互质的数的和; ②:2 x y  把第x个数变成y

思路:容斥原理求数区间[1..r]中与n互质的数的(个数&&和):HDU 4135

解决它的逆问题:求[1..r]中与n不互质的数的个数.

考虑n的素因子pi,则[1..r]中与pi不互质的数的个数是[r/pi].

然而,如果我们单纯将所有结果相加,会得到错误答案。有些数可能被统计多次(被好几个素因子整除)。所以,我们要运用容斥原理来解决。

我们可以用2^k的算法求出所有的pi组合,然后计算每种组合的pi乘积,通过容斥原理来对结果进行加减处理。

int solve(int r,int n){
int res = 0;
vector  p;
for (int i = 2; i * i <= n; i ++){
if (n % i == 0){
p.push_back(i);
while(n % i == 0){
n = n / i;
}
}
}
if (n > 1){
p.push_back(n);
}
for (int msk = 1; msk < (1 << p.size()); msk ++){
int mult = 1, bit = 0;
for (int i = 0; i < p.size(); i ++){
if (msk & (1 << i)){
++bit;
mult *= p[i];
}
}
int cur = r / mult;
if (bit % 2 == 1){
res += cur;
}
else    res -= cur;
}
return r - res;
}


回到此题中,求个数和求和是一样的,我们只需要考虑怎么处理第二个操作——因为这道题m给的很小,所以我们完全可以每次询问都枚举以前改变的情况,至此问题解决。注意一点就是同一个地方的数可能被改两次,所以用map映射处理比较好。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID(x,y) ((x+y)>>1)

using namespace std;

typedef long long LL;    //max long long == 9223372036854775807LL

map  m;

inline LL gcd(LL a, LL b){
return b ? gcd(b, a % b) : a;
}

inline LL solve(int r,int n){
if (r == 0) return 0;
LL rans = 0;
rans = (LL)r * LL(r + 1) / 2;
LL res = 0;
/*找n的素因子*/
vector  p;
for (int i = 2; i * i <= n; i ++){
if (n % i == 0){
p.push_back(i);
while(n % i == 0){
n = n / i;
}
}
if (n == 1) break;
}
if (n > 1){
p.push_back(n);
}
/*找n的素因子*/
for (int msk = 1; msk < (1 << p.size()); msk ++){
int mult = 1;
int bit = 0;
for (int i = 0; i < p.size(); i ++){
if (msk & (1 << i)){
++bit;
mult *= p[i];
}
}
LL cur = r / mult;
LL curans = 0;
curans = (cur + 1) * cur / 2 * mult;
if (bit % 2 == 1){
res += curans;
}
else    res -= curans;
}
return rans - res;
}

int main(){
int t, n, M, pnum;
scanf("%d",&t);
while(t--){
pnum = 0;
m.clear();
scanf("%d%d",&n,&M);
for (int i = 0; i < M; i ++){
int num;
scanf("%d",&num);
if (num == 2){
int a,b;
scanf("%d%d",&a, &b);
m[a] = b;
}
else{
int x,y,pp;
scanf("%d%d%d", &x, &y, &pp);
LL ans = solve(y, pp) - solve(x - 1, pp);
map  :: iterator it;
for (it = m.begin(); it != m.end(); it ++){
if (it->first >= x && it->first <= y){
if (gcd(it->first, pp) == 1){
ans -= it->first;
}
if (gcd(it->second, pp) == 1){
ans += it->second;
}
}
}
printf("%I64d\n",ans);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: