题意:给你n个不一样的数,m次询问区间l到r,问有多少对,其中一个数是另外一个数的因子。html
题解:树状数组+离线查询
这个题很重要的一点就是自身与自身也算一个对。node
在线不是很好弄,咱们离线处理,根据区间的变化更新区间内对数。ios
如何计算区间方案数呢?
咱们考虑区间
的方案数,记
的方案数 -
的方案数,但这是不够的,由于可能存在一个数在
中,另外一个在
中的状况,咱们记该种状况个数为
,那么区间方案数就是
。web
接下来考虑如何更新
和
。
用两个数组记录区间,一个
根据左端点排序,一个
根据右端点排序。
用来计算
,sum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1);
,由于基数在
左边这应该不难理解。
用来计算
,sum[qr[k].id] += su(qr[k].r) - su(qr[k].l - 1);
。数组
由于这题自身与自身也算一对,因此每一个数及其倍数只要更新一次。app
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<queue> #include<stack> #include<cmath> #include<vector> #include<fstream> #include<set> #include<map> #include<sstream> #include<iomanip> #define ll long long using namespace std; const int maxn = 2e5 + 5; int n, m, a[maxn]; struct node { int l, r, id; }ql[maxn], qr[maxn]; ll tree[maxn]; void add(int x, int num) { for (; x <= n; x += x & -x) tree[x] += num;; } int su(int x) { ll answer = 0; for (; x > 0; x -= x & -x) answer += tree[x]; return answer; } bool cmp1(node& x, node& y) { return x.l == y.l ? x.r < y.r : x.l < y.l; } bool cmp2(node& x, node& y) { return x.r == y.r ? x.l < y.l : x.r < y.r; } int vis[maxn]; int sum[maxn]; int main() { int ma = 0; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]), vis[a[i]] = i, ma = max(ma, a[i]); for (int i = 1; i <= m; i++) scanf("%d%d", &ql[i].l, &ql[i].r), qr[i].l = ql[i].l, qr[i].r = ql[i].r, ql[i].id = qr[i].id = i; sort(ql + 1, ql + m + 1, cmp1); sort(qr + 1, qr + m + 1, cmp2); for (int i = 1, j = 1, k = 1; i <= n; i++) { while (j <= m && ql[j].l == i) { sum[ql[j].id] -= su(ql[j].r) - su(ql[j].l - 1); j++; } //cout << sum[1] << " "; for (int x = a[i]; x <= ma; x += a[i]) { if (vis[x]) add(vis[x], 1); } while (k <= m && qr[k].r == i) { sum[qr[k].id] += su(qr[k].r) - su(qr[k].l - 1); k++; } //cout << sum[1] << endl; } for (int i = 1; i <= m; i++) printf("%d\n", sum[i]); return 0; }