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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章