hdu Sum 容斥定理的应用

http://acm.hdu.edu.cn/showproblem.php?pid=4407

比赛的时候想到了先求与p不互质的然后减去,但是就是遇到了容斥处理的问题,当时没有想到对因子容斥,只想到求和容斥那去了。。哎。悲剧啊、、

题意:

给出一个长度为n序列初始值为1,2,......n对其进行两种操作:

"2 x c"  将x位置的值替换成y;

 "1 x y p" 求区间[x,y]内与p互质的数的和。

思路:

首先对于2操作我们离线处理,因为只有对于1,2,3.....n这样的序列我们在求1操作时才好处理,首先对p进行因式分解,可知他最多有6个因子因为到17是就已经大于400000了,然后我们知道对于区间[1,n] 内能被p1整除的数目为n/p1  也即 p1,2*p1,3*p1....n/p1*p1。这是一个等差数列我们直接求个即可。假设p分解出来的质因子为p1,p2,p3.那么我们分别对其用等差数列求和然后容斥掉有公共因子的即可。

 

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Max(a , b) ((a) > (b) ? (a) : (b))

#define ll __int64
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 400007
using namespace std;
//freopen("din.txt","r",stdin);

int p[660],prim[660],idx;

int tmp[30];

map<int,int>mp;
int n,m;
//sqrt(400000)=632....枚举出660以内的质数即可
void getPrime(){
    int i,j;
    CL(p,0);
    for (i = 2; i*i < 660; ++i){
        if (!p[i]){
            for (j = i + i; j < 660; j += i)
            p[j] = 1;
        }
    }
    idx = 0;
    for (i = 2; i < 660; ++i){
        if (!p[i]) prim[idx++] = i;
    }
}
ll getS(int x){
    return ((1ll + (ll)x)*(ll)x)/2;
}
int gcd(int a,int b){
    if (b == 0) return a;
    return gcd(b,a%b);
}
ll cal(int p,int x){
    int i,j;
    int len = 0;
    //分解质因子
    for (i = 0; i < idx; ++i){
        if (p % prim[i] == 0){
            tmp[len++] = prim[i];
            while (p%prim[i] == 0){
                p /= prim[i];
            }
        }
        if (p == 1) break;
    }
    if (p != 1) tmp[len++] = p;
    
    //状态压缩+容斥
    ll ans = 0;
    for (i =  1; i < (1<<len); ++i){
        int ct = 0;
        int fac = 1;
        for (j = 0; j < len; ++j){
            if (i&(1<<j)){
                fac *= tmp[j];
                ct++;
            }
        }
        int k = x/fac;
        if (ct&1){
            ans += (((ll)fac + (ll)k*fac)*(ll)k)/2;
        }
        else{
            ans -= (((ll)fac + (ll)k*fac)*(ll)k)/2;
        }
    }
    return ans;
}
int main(){
    //freopen("din.txt","r",stdin);
    getPrime();
    int t;
    map<int,int>::iterator it;
    int op,x,y,p;
    scanf("%d",&t);
    while (t--){
        mp.clear();
        scanf("%d%d",&n,&m);
        while (m--){
            scanf("%d",&op);
            if (op == 2){
                scanf("%d%d",&x,&y);
                mp[x] = y;//map来确定最后x为之修改成了y
            }
            else{
                scanf("%d%d%d",&x,&y,&p);
                ll res = 0;
                res = getS(y) - getS(x - 1) - (cal(p,y) - cal(p,x - 1));//先求[x,y]区间的值,然后减去[x,y]与p不互质的数
                //更新修改后的,离线处理
                for (it = mp.begin(); it != mp.end(); ++it){
                    if (it->first != it->second && it->first >= x && it->first <= y){
                        if (gcd(it->first,p) == 1) res -= it->first;
                        if (gcd(it->second,p) == 1) res += it->second;
                    }
                }
                printf("%I64d\n",res);
            }
        }
    }
    return 0;
}

 

 

posted @ 2012-09-23 10:35  E_star  阅读(284)  评论(0编辑  收藏  举报