您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: