hdu 4407 容斥原理

刚开始看的时候以为是线段树,怎么想也想不通,看了思路之后,才知道条件给的很优越,用容斥原理统计区间和,由于m不大,再用辗转相除统计改变的数,注意用64位整数。

 

ACcode:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using std::map;

typedef long long LL;
const int nsize=440000;
const int msize=1111;

map <int,int> c;
map <int,int>::iterator ct;

int factor[110],cnt;

int gcd(int aa,int bb)
{
    return bb==0?aa:gcd(bb,aa%bb);
}

int main()
{
    LL sum,res;
    int a,b,k,num,s,w;
    int T,n,m,op,p,q,x,y,ans;
    scanf("%d",&T);
    while (T--)
    {
        c.clear();
        scanf("%d %d",&n,&m);
        for (int i=0;i<m;i++)
        {
            scanf("%d",&op);
            if (op==1)
            {
                scanf("%d %d %d",&x,&y,&p);
                if (x>y) k=x,x=y,y=k;
                res=0,cnt=0,q=p;
                for (int j=2;j<=(int)(sqrt((double)p));j++)
                {
                    if (p%j==0)
                    {
                        factor[cnt++]=j;
                        while (p%j==0) p/=j;
                    }
                }
                if (p>1) factor[cnt++]=p;
                p=(1<<cnt);
                for (int j=0;j<p;j++)
                {
                    ans=w=0,k=j,s=1;
                    while (k>0)
                    {
                        if (k&1) ans++,s*=factor[w];
                        k>>=1,w++;
                    }
                    a=(x-1)/s*s+s;
                    b=y/s*s;
                    sum=(LL)(a+b)*((b-a)/s+1)/2;
                    if (ans&1) res-=sum;
                    else res+=sum;
                }
                for (ct=c.begin();ct!=c.end();ct++)
                {
                    a=ct->first,b=ct->second;
                    if (a<x||a>y) continue;
                    if (gcd(a,q)==1) res-=a;
                    if (gcd(b,q)==1) res+=b;
                }
                printf("%I64d\n",res);
            }
            else
            {
                scanf("%d %d",&x,&y);
                c[x]=y;
            }
        }
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值