HDU 4407-容斥原理+暴力+欧拉函数

该博客讨论了一道题目,涉及数列{An}的两种操作:求区间内与特定数p互质的数之和,以及改变数列中元素的值。由于操作次数较少,可以暴力处理。博主利用欧拉函数计算[1, x]内与p互质的数的数量,并通过容斥原理解决问题。在存在修改操作的情况下,通过遍历修改过的元素并更新答案来处理。" 78779086,7321121,OGNL:Java表达式引擎详解,"['OGNL', '表达式引擎', 'Java框架', '数据转换', '对象构造']
摘要由CSDN通过智能技术生成

题意: 有一个元素为 1~n (n<=4e5)的数列{An},有2种操作,m次(1000次): 

1、求某段区间 [a,b] 中与 p 互质的数的和。 

2、将数列中某个位置元素的值改变。


这题因为m很小,所以可以暴力处理m,那么我们先考虑元数组为1-n,问题就简单多了


先求出【1,x】内 与p互质的数有多少个,根据欧拉定理就可以求啦,也就是求出p的质数,然后容斥一下得到与p互质的数的个数。


num1=get(1,x-1),num2=get(1,y),  如果没有修改操作的话,ans应该就是num2-num1,但是因为m操作太少了,我们直接暴力记录一下操作,把修改过的元素下标存到set,每次遍历处于【x,y】之间的修改过的下标,如果原来这个数与p互质,答案减1。如果修改后的数与p互质,答案加1.


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const long long maxn=400010;
set<long long> s;
set<long long>::iterator it;
long long  px[10];
long long n,m;
long long a[maxn],prime[maxn],tot=0;
bool flag[maxn];

long long gcd(long long a,long long b)
{
    if (!b) return a;
    return gcd(b,a % b);
}

long long get(long long x,long long p)
{
     long long k=0,pp=p;
    for (long long i=1; prime[i]*prime[i]<=pp; i++)
    {
        if (flag[pp]) break;
        if (pp % prime[i]==0)
        {
            px[k++]=prime[i];
            while (pp % prime[i]==0)
                pp/=prime[i];
        }
    }
    if (pp!=1)
        px[k++]=pp;
    long long tmp=0;
    for (long long state=1; state<(1<<k); state++)
    {
        long long num=0,q=1;
        for (long long j=0; j<k; j++)
            if (state & (1<<j))
            {
                num++;
                q*=px[j];
            }
        long long num2=x/q;
        if (num % 2)
            tmp=tmp+num2*(q+q*num2)/2;
        else
            tmp=tmp-num2*(q+q*num2)/2;
    }
    return (1+x)*x/2-tmp;
}


int main()
{
    //freopen("input.txt","r",stdin);
    long long tt;scanf("%lld",&tt);
    memset(flag,1,sizeof(flag));
    for (long long i=2; i<=400000; i++)
    {
        if (flag[i])
        {
            prime[++tot]=i;
            for (long long j=i*i; j<=400000; j+=i)
                flag[j]=0;
        }
    }

    while (tt--)
    {
        scanf("%lld%lld",&n,&m);
        s.clear();
        memset(a,0,sizeof(a));
        for (long long i=1; i<=m; i++)
        {
            long long op,x,y,p,c;
            scanf("%lld",&op);
            if (op==1)
            {
                scanf("%lld%lld%lld",&x,&y,&p);
                long long num1=get(x-1,p);
                long long num2=get(y,p);
                long long tmp=num2-num1;
                it=s.lower_bound(x);
                for (; it!=s.end(); it++)
                    if (*it>=x && *it<=y)
                    {
                        if (gcd(p,*it)==1)
                            tmp-=(*it);
                        if (gcd(p,a[*it])==1)
                            tmp+=a[*it];
                    }
                    else
                        break;
                printf("%lld\n",tmp);
            }
            else
            {
                scanf("%lld%lld",&x,&c);
                s.insert(x);
                a[x]=c;
            }
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值