HDU 4407 Sum

XXX is puzzled with the question below: 

1, 2, 3, ..., n (1<=n<=400000) are placed in a line. There are m (1<=m<=1000) operations of two kinds. 

Operation 1: among the x-th number to the y-th number (inclusive), get the sum of the numbers which are co-prime with p( 1 <=p <= 400000). 
Operation 2: change the x-th number to c( 1 <=c <= 400000). 

For each operation, XXX will spend a lot of time to treat it. So he wants to ask you to help him. 

题目大意:

有m次操作,操作1:求x~y之间与p互质的数的和,操作2:把第x个数字改为c,(注意是第x个数)

解题思路:

求区间和又有单点更新,可能会想到线段树,但是这个题考的是容斥原理

先考虑操作1:把p分解成素数因子,利用容斥原理我们可以得到1~x区间内与p互质数的和,所以answer就是sum(y)-sum(x),然后再单独判断一下x(因为x在两个区间里面重复计算了),这个是不考虑更新的;

对于操作2:我们把每一次的操作存在map里面(用数组或者pair封装的vector会错,因为可能存在一个点多次更新),m比较小(不会超时),所以每一次输出前遍历一遍map,判断是否需要更新,这个比较简单了

注意细节:在利用容斥原理计算时,比如在1~100内,求2*3的倍数的数字的和,100/6就是个数,也是最后一个数,sum=2*3(1+100/(2*3))*(100/(2*3))/2;也就是等差数列求和

最后就是求的sum会溢出,用long long存就好

代码还是很详细的..=v=..

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
map<int,int>mp;
int fac[200];
void update()
{
    int a,b;
    scanf("%d%d",&a,&b);
    mp[a]=b;
}
int div(int p)//分解p
{
    int cnt=0;
    for(int i=2;i*i<=p;i++)
    {
        if(p%i==0)
            fac[cnt++]=i;
        while(p%i==0) p/=i;
    }
    if(p>1) fac[cnt++]=p;
    return cnt;
}
bool co_prime(int n,int cnt)
{
    for(int i=0;i<cnt;i++)
    {
        if(n%fac[i]==0)
            return 0;
    }
    return 1;
}
ll get_mulsum(ll mul,int n)
{
    ll sum=mul*(1+n/mul)*(n/mul)/2;
    return sum;
}
ll rc(int x,int cnt)//得到[1,x]区间里面与p互质的数的和
{
    ll sum1,sum2;
    sum1=sum2=0;
    for(int i=1;i<(1<<cnt); i++)
    {
        ll ones=0,mul=1;
        for(int j=0;j<cnt;j++)
        {
            if(i & (1<<j))
            {
                mul*=fac[j];
                ones++;
            }
        }
        ll part_sum=get_mulsum(mul,x);
        if(ones&1)
            sum1+= part_sum;
        else
            sum1-= part_sum;
    }
    return (ll)(1+x)*x/2-sum1;

}
ll get_sum()
{
    int x,y,p,cnt;
    ll sum1,sum2,sum;
    scanf("%d%d%d",&x,&y,&p);
    cnt=div(p);
    sum1=rc(x,cnt);//得到的是1~x与p互质的数的和
    sum2=rc(y,cnt);//1~y的和
//    printf("sum1=%lld,sum2=%lld\n",sum1,sum2);
    sum=sum2-sum1;
    if(co_prime(x,cnt)) sum+=x;//计算了两次
    //scan operaton 2
    map<int,int>::iterator it;
    for(it=mp.begin();it!=mp.end();it++)
    {
        if(it->first >=x && it->first <=y)
        {
            if(co_prime(it->first,cnt))
             sum-=it->first;
             if(co_prime(it->second,cnt))
             sum+=it->second;
        }
    }
    return sum;
}
int main()
{
   int t,n,m;
   scanf("%d",&t);
   while(t--)
   {
       int op;
       scanf("%d%d",&n,&m);
       mp.clear();
       for(int i=0;i<m;i++)
       {
           scanf("%d",&op);
           if(op==1)
                printf("%lld\n",get_sum());
           else
                update();
       }
   }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值