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;

}

 

 

你可能感兴趣的:(HDU)