CF 301D Yaroslav and Divisors
题意:给出n的一个排列,每次询问一个区间内有多少对数满足一个数是另一个数的因数。(n<=2e5)
将询问离线排序后从左往右扫,每次对于当前位置的数找它所有倍数出现的位置p,如果p<= i,则在i位置上加一,否则在p位置上记录i,当再次扫到p时在i位置上加一。每次扫描到区间右端点时用树状数组统计答案。
#include<bits/stdc++.h> #define N 200020 using namespace std; int a[N], b[N], c[N], r[N], l[N], ans[N], n, m; vector<int> ask[N]; vector<int> d[N]; void add(int x) { while(x<= n) { c[x]++; x+= x&-x; } } int sum(int x) { int s= 0; while(x) { s+= c[x]; x-= x&-x; } return s; } int main() { while(scanf("%d%d", &n, &m)== 2) { for(int i= 1; i<= n; i++) { scanf("%d", &a[i]); b[a[i]]= i; } for(int i= 1; i<= m; i++) { scanf("%d%d", &l[i], &r[i]); ask[r[i]].push_back(i); } for(int i= 1; i<= n; i++) { for(int j= 0; j< (int)d[i].size(); j++) add(d[i][j]); for(int j= 1; j* a[i]<= n; j++) { int p= b[j* a[i]]; if(p<= i) add(p); else d[p].push_back(i); } for(int j= 0; j< (int)ask[i].size(); j++) { int p= ask[i][j]; ans[p]= sum(r[p])- sum(l[p]- 1); } } for(int i= 1; i<= m; i++) printf("%d ", ans[i]); } return 0; }