HDU6102(树状数组 + 容斥 + 离线处理)
2017-08-12 09:43
302 查看
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=LR∑j=i+1R∑k=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
1≤T≤100
1≤N,M≤100000
1≤Pi≤N,
for any pair (i,j), Pi≠Pj
1≤Li≤Ri≤N
About 95% test case: 1≤N≤1000
Other test case: 1≤N≤100000
Output
As for each case, you need to output M integer which indicate the answer of each query.
Sample Input
2
3 1
3 2 1
1 3
6 3
6 5 4 3 2 1
1 6
2 6
3 5
Sample Output
1
8
5
0
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; }
相关文章推荐
- HDU 3874 Necklace 树状数组 + 离线处理
- POJ - 1990 MooFest解题报告(树状数组+离线处理)
- POJ-3416 Crossing (树状数组 离线处理)
- HDU 4638 Group (线段树 | 树状数组 + 离线处理)
- HDU 4777 Rabbit Kingdom(树状数组离线处理)
- bzoj1878: [SDOI2009]HH的项链(树状数组+离线处理)
- hdu 4417 树状数组 离线处理
- HDU 4417 Super Mario (树状数组、离线处理)
- hdu 3874 树状数组 离线处理
- HDU 4417 Super Mario(树状数组离线处理 or 主席树)
- HDU 3874 Necklace(树状数组离线处理)
- 51Nod1463 找朋友(离线处理+树状数组维护区间最大值)
- HDU 4777 Rabbit Kingdom(树状数组离线处理)
- hdu 4630 多校第二场 树状数组,离线处理
- HH的项链(树状数组+离线处理)
- hdu-4630-No Pain No Game-(树状数组,离线处理)
- SPOJ DQUERY(树状数组离线处理 or 主席树 区间不同数个数)
- hdu 5057 Argestes and Sequence(离线处理树状数组)
- hihoCoder 1145 幻想乡的日常(树状数组 + 离线处理)
- ZOJ 4008 Yet Another Tree Query Problem 树状数组 离线处理