51nod 1678(容斥原理)

7 篇文章 0 订阅
5 篇文章 0 订阅
/*
     51nod 1678
     这天,lyk又和gcd杠上了。
     它拥有一个n个数的数列,它想实现两种操作。
     1:将 ai 改为b。
     2:给定一个数i,求所有 gcd(i,j)=1 时的 aj 的总和。


     直接暴力做超时
     而且直接求互质的数不好求
     所以我们考虑求不互质的数
     然后用容斥原理
     用容斥原理前我们先把每个数的倍数
     所对应的那个数的和求出来存入tmp数组
     如tmp[i]就表示i的倍数所对应的数求和
     所以我们在更改的时候也要把这个更改
     接下来就是容斥原理
*/
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <string.h>
#define mod 1000000007
#define MAX 100005
#define ll long long
#define PI acos(-1)
using namespace std;


int p[100005];
int prime[100005];
int k;
inline void isprime()//筛选出素数
{
    k=0;
    memset(prime,0,sizeof(prime));
    for(int i=2; i*i<100002; i++)
    {
        if(!prime[i])
        {
            p[k++]=i;
            for(int j=i*i; j<100005; j=j+i)
                prime[j]=1;
        }
    }
}
int a[100005];//存一开始给的n个数
int tmp[100005];//tmp[i],表示为所有是 i 的倍数的数字的和
int b[1005];//存质因数
int main()
{
    isprime();
    int n,Q;
    while(scanf("%d%d",&n,&Q)!=EOF)
    {
        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        memset(tmp,0,sizeof(tmp));
        for(int i=2;i<=n;i++)//将i的倍数相加
            for(int j=1;i*j<=n;j++)
                tmp[i]+=a[i*j];
        while(Q--)
        {
            int A;
            scanf("%d",&A);
            if(A==1)
            {
                int s,w;
                scanf("%d%d",&s,&w);
                sum-=a[s];
                tmp[s]-=a[s];
                tmp[s]+=w;
                for(int i=2;i*i<=s;i++)
                {
                    if(i*i==s)//当s为平方数有一种特殊情况
                    {
                        tmp[i]-=a[s];
                        tmp[i]+=w;
                        continue;
                    }
                    if(s%i==0)
                    {
                        tmp[i]-=a[s];
                        tmp[i]+=w;
                        tmp[s/i]-=a[s];
                        tmp[s/i]+=w;
                    }
                }
                a[s]=w;
                sum+=w;
            }
            else
            {
                int s;
                scanf("%d",&s);
                int cut=0,num=s;
                for(int i=0;p[i]*p[i]<=num&&i<k;i++)//质因数分解
                {
                    if(num%p[i]==0)
                        b[cut++]=p[i];
                    while(num%p[i]==0)
                        num/=p[i];
                }
                if(num>1)
                    b[cut++]=num;
                int ans=0;
                for(int i=1;i<(1<<cut);i++)//用状态压缩自由组合素因子
                {
                    int res=1,flag=0;
                    for(int j=0;j<cut;j++)
                    {
                        if(((i>>j)&1)==1)
                        {
                            res*=b[j];
                            flag++;
                        }
                    }
                    if(flag%2)//容斥
                        ans=ans+tmp[res];
                    else
                        ans=ans-tmp[res];
                }
                cout<<sum-ans<<endl;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值