HDU4407 Sum【容斥原理】

题目链接:

http://acm.hdu.edu.cn/showproblem.php?

pid=4407


题目大意:

刚開始有一个长度为N的序列,排列为{1、2、3、…、N}。

下边是M个操作指令。

有两种操作:

操作1:给你三个整数 Left Right d  求出区间[Left。Right]上与整数d互素的数的和

操作2:给你两个整数 pos d  将第pos个位置上的数改为d。


解题思路:

求出区间[Left,Right]上与整数d互素的数的和能够用容斥定理求出,类似于HDU4135

下边考虑更改操作。看了题意,1 <= M <= 1000。最多仅仅有1000个操作,那么能够把

每次的更改操作都保存起来。在求[Left,Right]上与整数d互素的数的和时,能够先求出

原先未改变时的结果,再加上改变操作中改变的结果,即为终于结果。详细參考代码。

參考博文:http://blog.csdn.net/ok_again/article/details/11167313


AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define LL __int64
using namespace std;

int GCD(int a,int b)
{
    if(b == 0)
        return a;
    return GCD(b,a%b);
}

int Factor[22],ct;
int A[1100],B[1100];
//A[]纪录更改位置(下标) B[]纪录更改的值
void Divide(int N)
{
    ct = 0;
    for(int i = 2; i <= sqrt(N*1.0); ++i)
    {
        if(N % i == 0)
        {
            Factor[ct++] = i;
            while(N % i == 0)
                N /= i;
        }
    }
    if(N != 1)
        Factor[ct++] = N;
}

LL Solve(int Left, int Right)
{
    LL ans = 0;
    for(int i = 1; i < (1 << ct); ++i)
    {
        int odd = 0;
        int tmp = 1;
        for(int j = 0; j < ct; ++j)
        {
            if((1 << j) & i)
            {
                odd++;
                tmp *= Factor[j];
            }
        }
        LL Num = Right / tmp - (Left-1) / tmp;  //区间[L,R]中因子tmp的倍数个数
        LL Cnt = Num * (Num+1) / 2 * tmp + ((Left-1)/tmp) * tmp * Num;  //[L,R]上因子tmp的倍数和
        if(odd & 1) //奇加偶减
            ans += Cnt;
        else
            ans -= Cnt;
    }
    return (LL)(Left+Right) * (LL)(Right-Left+1) / 2 - ans; //得到区间[L,R]与d互质的数的和
}

int main()
{
    int T,N,M,op;
    int Left,Right,d,pos;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        int id = 0;
        while(M--)
        {
            scanf("%d",&op);
            if(op == 1)
            {
                scanf("%d%d%d",&Left,&Right,&d);
                Divide(d);
                LL ans = Solve(Left,Right);
                for(int i = 0; i < id; ++i)
                {
                    if(A[i] >= Left && A[i] <= Right)
                    {
                        if(GCD(d,A[i]) == 1) //由于原先第i位置上的数为i。

计算[L,R]中互质数已经计算过一次,所以要减去 ans -= A[i]; if(GCD(d,B[i]) == 1) //假设更改的数与d互质。则加上该数。

ans += B[i]; } } printf("%I64d ",ans); } else { int Flag = 0; scanf("%d%d",&pos,&d); for(int i = 0; i < id; ++ i) { if(A[i] == pos) { Flag = 1; B[i] = d; break; } } if(!Flag) { B[id] = d; A[id++] = pos; } } } } return 0; }



原文地址:https://www.cnblogs.com/wgwyanfs/p/7156803.html