HDU6102(树状数组 + 容斥 + 离线处理)

来源:互联网 发布:淘宝装修店铺模板 编辑:程序博客网 时间:2024/06/07 18:00

GCDispower

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 141    Accepted Submission(s): 70


Problem Description
There is a mysterious organization called GCD. They get a permutation P, permutation is a sequence of length N, only consists of number 1 to N and each number appears only once.
They want to gain magic power from this permutation. For the interval [L, R] they can get power:
i=LRj=i+1Rk=j+1R(gcd(P[i],P[j])==P[k])P[k]
Now they have M queries, for each query can you help them calculate how many power they can get?
 

Input
The first line is an integer T which indicates the case number.
And as for each case, first line is two integer N and M which indicates the length of permutation and number of query.
Next line is N integer which indicate the permutation P
Next M line, for each line is two integer L and R which indicate each query.

Limit
1T100
1N,M100000
1PiN, for any pair (i,j)PiPj
1LiRiN

About 95% test case: 1N1000
Other test case: 1N100000
 

Output
As for each case, you need to output M integer which indicate the answer of each query.
 

Sample Input
23 13 2 11 36 36 5 4 3 2 11 62 63 5
 

Sample Output
1850
 

Source
2017 Multi-University Training Contest - Team 6
题意:给你一个序列,然后给你若干个询问,每个询问一个区间[l,r],问你这个区间的价值是多少,一个区间的价值我们定义为,若这个区间存在一个三元组i, j, k,使得gc(a[i], a[[j]) == a[k],则这个区间的价值加上a[k],(刚开始为0)。

解题思路:我们肯定是要把这些询问离线处理,然后一个最重要的一点是要枚举a[k],从左到右,我们可以知道如果gcd(a[i], a[j])  == a[k],则a[i] / a[k], 与a[j] / a[k] ,一定互质,所以我们肯定要枚举a[k]的倍数,并且位置在k的前面,处理出所有的倍数之后,我们从右往左扫,假如我们每次知道当前有多少对互质的数假设为x,我们就知道对区间左端点的贡献为x * a[k],我们可以用一个树状数组,或者线段树来维护一下就行, 关键是怎样知道当前有多少对互质的数,我们可以用容斥求出,我们可以每次记录一个数进行素数分解之后的所有可能的质因子乘积,然后后面的数通过容斥原理,先进行素数分解,然后枚举gcd为他的每一个质因子的倍数,这些数一定与当前数不互质,用总的数的个数减去这些数就行,我们可以用容斥求这些集合的并。

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 100000 + 10;int N, M;vector<int> prime[maxn];//素数分解,之后的质因子bool valid[maxn];//素数筛选int a[maxn];//原数组int Hash[maxn];//元素i在原数组的位置vector<pair<int, int>> query[maxn];//query[i]表示以i为右端点的区间,query[i][j].first表示询问id,query[i][j].second表示询问区间的左端点ll ans[maxn];//用于容斥的数组,ans[i]表示i在之前出现过的次数vector<pair<int, int>> status[maxn];//stastus[i][j].first表示状态的大小,second表示容斥的符号ll Tree[maxn];//树状数组ll result[maxn];//保存结果int temp[maxn];//辅助数组int lowbit(int x){    return x&(-x);}void add(int loc, ll value){    for(int i = loc; i <= N; i += lowbit(i))    {        Tree[i] += value;    }}void update(int l, int r, ll value){    add(l, value);    add(r + 1, -value);}ll get(int loc){    ll res = 0;    for(int i = loc; i >= 1; i -= lowbit(i))    {        res += Tree[i];    }    return res;}void updateAns(int x, ll value){    for(int i = 0; i < status[x].size(); i++)    {        ans[status[x][i].first] += value;    }}ll getAns(int x){    ll res = 0;    for(int i = 0; i < status[x].size(); i++)    {        res += ans[status[x][i].first] * status[x][i].second;    }    return res;}void initPrime(){    memset(valid, true, sizeof(valid));    for(int i = 0; i < maxn; i++)    {        prime[i].clear();        status[i].clear();    }    for(int i = 2; i < maxn; i++)    {        if(valid[i])        {            for(int j = i; j < maxn; j += i)            {                valid[j] = false;                prime[j].push_back(i);            }        }    }    for(int i = 1; i < maxn; i++)    {        for(int j = 0; j < (1<<(prime[i].size())); j++)        {           status[i].push_back(make_pair(1, 1));           for(int k = 0; k < prime[i].size(); k++)           {               int num = (j>>k)&1;               if(num)               {                   status[i][j].first *= prime[i][k];                   status[i][j].second *= -1;               }           }        }    }}void init(){    memset(Tree, 0, sizeof(Tree));    memset(ans, 0, sizeof(ans));    for(int i = 1; i <= N; i++)    {        query[i].clear();    }}int main(){    initPrime();    //cout<<"initPrime sucess"<<endl;    int T;    scanf("%d", &T);    while(T--)    {        scanf("%d%d", &N, &M);        init();        for(int i = 1; i <= N; i++)        {            scanf("%d", &a[i]);            Hash[a[i]] = i;        }        int l, r;        for(int i = 1; i <= M; i++)        {            scanf("%d%d", &l, &r);            query[r].push_back(make_pair(i, l));        }        for(int i = 1; i <= N; i++)//枚举左端点,计算对右端点的贡献        {            int cnt = 0;            for(int j = 2 * a[i]; j <= N; j += a[i])//枚举a[i]的倍数            {                if(Hash[j] < i) temp[++cnt] = Hash[j];            }            sort(temp + 1, temp + cnt + 1);//对位置进行排序,便于从右往左扫            ll sum = 0;            temp[0] = 0;            for(int j = cnt; j >= 1; j--)            {                sum += getAns(a[temp[j]] / a[i]) * a[i];                updateAns(a[temp[j]] / a[i], 1);//更新容斥                if(j != 1)                update(temp[j - 1] + 1, temp[j], sum);//更新对左区间的贡献                else if(temp[1] == 1) update(1, 1, sum);                else update(1, temp[1], sum);            }            for(int j = cnt; j >= 1; j--)            {                updateAns(a[temp[j]] / a[i], -1);//还原容斥            }            for(int j = 0; j < query[i].size(); j++)//计算结果            {                result[query[i][j].first] = get(query[i][j].second);            }        }        for(int i = 1; i <= M; i++)        {            printf("%lld\n", result[i]);        }    }    return 0;}




原创粉丝点击