Sum HDU - 4407(容斥定理)

Sum HDU - 4407

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.
Input
There are several test cases.
The first line in the input is an integer indicating the number of test cases.
For each case, the first line begins with two integers — the above mentioned n and m.
Each the following m lines contains an operation.
Operation 1 is in this format: “1 x y p”.
Operation 2 is in this format: “2 x c”.
Output
For each operation 1, output a single integer in one line representing the result.
Sample Input

1
3 3
2 2 3
1 1 3 4
1 2 3 6

Sample Output

7
0

题意:

给一个数n,初始序列为1-n连续的数
两种操作
1.三个数x,y,p,求区间x-y和p互质的数的和
2.两个数x,y,将x位置的数修改成y

分析:

首先我们知道通过容斥定理来求区间[1,n]中和p互质的个数是能求的

我们先把p分解质因数,然后二进制枚举质因数组合得到一个因数,然后看n中有多少个数是这个因数的倍数,通过容斥加加减减,得到个数的答案

这个是求个数的过程那么求和呢?只需要加一个等差数列求和就行了,因为这个我们枚举一个因数v,无非就是看n中有多少数是v的倍数即v的1倍2倍3倍。。。,所以将共同v提出来就是1,2,3…所以就是等差数列求和, k × ( k + 1 ) 2 × v \frac{k\times (k+1)}{2}\times v

那么这样给定一个范围x我们很容易求出1-x范围内与p互质的数的和,那么这道题给定的原始序列就是1-n连续的数,所以首先我们可以先求这个,对于修改的位置,因为并不是很多,我们检查一下我们所求的1-x的区间范围内有没有修改的数,我们检查一下,如果原始数(位置标号)和p不互质,不用管,因为上面容斥的的时候一定没加这个数,而如果发现互质,说明上面容斥的时候加上了,但是这个地方已经被修改了,所以首先我们需要减去这个原始的数(位置数),然后我们在判断一下修改之后的数是否和p互质,如果互质我们还需要在加上

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 400010;
int n,m;
struct node{
    int idx,v;//idx表示原始位置(原始数),v表示修改后的数
    node(){};
    node(int x,int y):idx(x),v(y){};
};
vector<node> c;//记录修改点
bool is[maxn];
vector<int>pr;
void prime(){
    memset(is,true,sizeof(is));
    pr.clear();
    pr.push_back(2);
    for(int i = 3; i < maxn; i += 2){
        if(is[i]){
            pr.push_back(i);
            for(int j = i + i; j < maxn; j += i){
                is[j] = false;
            }
        }
    }
}
ll cal(int x,int p,vector<int>&g){
    ll ret = (ll)x * (x + 1) / 2;//因为这道题是求和,而且我们只考虑原始数列1-n连续数字,应该是等差数列求和,所以这是原始总和
    //容斥部分
    for(int s = 1; s < (1 << g.size()); s++){
        int cnt = 0;
        int v = 1;
        for(int i = 0; i < g.size(); i++){
            if(s & (1 << i)){
                cnt++;
                v *= g[i];
            }
        }
        ll k = x / v;//这里是1-x内是v的倍数的个数
        k = k * (k + 1) * v / 2;//等差数列求和(原来是1,2,3..现在只是可以提出个公倍数v)
        if(cnt % 2 == 1) ret -= k;
        else ret += k;
    }
    //上面是求出了区间1-x连续的不带修改的情况下的和,下面枚举修改部分
    for(int i = 0; i < c.size(); i++){
        if(c[i].idx <= x){//说明找到一个修改位置在我们求的1-x范围内
            if(__gcd(c[i].idx,p) == 1) ret -= c[i].idx;//如果原本这个数和p互质说明我们上面容斥的时候已经加上了,现在要减去
            if(__gcd(c[i].v,p) == 1) ret += c[i].v;//如果新修改的值和p互质我们需要再加上这个值
        }
    }
    return ret;
}
void work(int x,int y,int p){
    vector<int> g;
    int k = p;
    for(int i = 0; i < pr.size() && pr[i] <= k; i++){//存下p的所有质因子
        if(k % pr[i] == 0){
            g.push_back(pr[i]);
            while(k % pr[i] == 0){
                k /= pr[i];
            }
        }
    }
    printf("%lld\n",cal(y,p,g) - cal(x-1,p,g));//利用容斥求[1,x-1]的再求[1,y]的相减得到
}
int main(){
    prime();
    int T,x,y,o,p;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        c.clear();
        while(m--){
            scanf("%d",&o);
            if(o == 1){
                scanf("%d%d%d",&x,&y,&p);
                work(x,y,p);
            }
            else{
                scanf("%d%d",&x,&y);
                bool f = true;
                for(int i = 0; i < c.size(); i++){//先看一下原来这个位置是否修改过,如果改过为了防止重复就不用再往里放新的结构体了
                    if(c[i].idx == x){//直接修改这个这个结构体就行了
                        c[i].v = y;
                        f = false;
                        break;
                    }
                }
                if(f) c.push_back(node(x,y));//如果这个位置第一次修改就放入vector
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/82800090
今日推荐