Codeforces 301D (182 D) Yaroslav and Divisors
問題
1以上n以下のn個の整数a[i]が与えられる。
a[i]は全て互いに異なる。
このa[i]に対して、m個の以下のクエリに答えよ。
整数l, rが与えられる。
l≦i, j≦rなるi, jで、a[i]がa[j]の倍数であるものの個数を求める。
制約条件
n≦200000
クエリの個数≦200000
方針
ペアの個数は最大で、
n + n / 2 + n /3 + ... なのでO(nlogn)程度。具体的には250万個以下。
a[i]がa[j]の倍数であるような(i, j)を平面上に点として取ると、クエリは、
x座標、y座標がともにl以上r以下の点の個数を求めよと言っていることになる。
(i, j)をi≦jとしておけば、
x座標がl以上y座標がr以下の点の個数を求めればよくなる。
これは、よくある問題で、クエリをソートして、点をうちながら
bitを使ってx座標がr以下の点を数えるようにすれば点の個数Nに対してO(NlogN)でできる。
ソースコード
const int MX = 2500000; int bit[MX]; inline int sum(int i){ int res = 0; for(; i; i -= i & -i) res += bit[i]; return res; } inline void add(int i, int x){ for(; i < MX; i += i & -i) bit[i] += x; } int n, m, a[200000], ans[200000]; int pos[200001]; int main(){ scanf("%d%d", &n, &m); rep(i, n) scanf("%d", a + i), pos[a[i]] = i; vector<pi> ps; for(int i = 1; i <= n; i++){ for(int j = i; j <= n; j += i){ int a = pos[i], b = pos[j]; if(a > b) swap(a, b); ps.pb(mp(a, b)); } } vector<pair<pi, int> > v; rep(i, m){ int l, r; scanf("%d%d", &l, &r); v.pb(mp(mp(l - 1, r - 1), i)); } sort(all(v)); sort(all(ps)); for(int i = m - 1, j = ps.size() - 1; i >= 0; i--){ while(j >= 0 && ps[j].first >= v[i].first.first){ add(ps[j].second + 1, 1); j--; } ans[v[i].second] = sum(v[i].first.second + 1); } rep(i, m) printf("%d\n", ans[i]); return 0; }