目录
F. Nobody is needed:
题目大意:
思路解析:
现有一次查询,给出一个区间 [l,r] 问满足要求的索引集的个数有多少个。
索引集应满足 l <= ti <= r, ti < ti+1, ati+1 % ati == 0。
如果没有区间的限制,我们其实可以很容易的得到满足第二个和第三个要求的索引集数目。这可以用dp完成,因为整除,那么一定是整数倍,所以这个转移复杂度就会整体退化为 n * logN * logN。这样就可以得到所有满足要求的索引数,现在我们再考虑如何让他满足第一个要求。
假设现在有一个数组 2 1 6 3 5 4 8 7,查询区间 [2, 3] 那么就是子数组 [1 6] 有多少个满足要求的索引集,现在我们利用 pos[i],代表i的位置,dp[i] 表示当i作为索引集的最后一个数时,有多少个满足要求的索引集。那我们先整体将这个查询离线处理,每次固定 l,进行查询。那我们可以想到从后往前遍历 l,r用pos[i]来约束,那么我们在将这些得到的数据插入到树状数组,每次离线查询即可。
也可以翻转整个数组,这样就可以固定r,从前往后遍历,其余操作和上诉描述的类似,可以自己手动模拟一下,这里讲的比较抽象,可以画图理解。
代码实现 :(我用的翻转数组的方法)
import java.io.*;
import java.math.BigInteger;
import java.util.*;
public class Main {
static int inf = (int) 2e7;
static class Fenwick{
long[] t;
int n;
public Fenwick(int n) {
t = new long[n];
this.n = n;
}
void add(int x, int v){
for (int i = (x + 1); i <= n; i += i & -i) {
t[i - 1] += v;
}
}
long sum(int x){
long res = 0;
for (int i = (x + 1); i > 0; i -= i & -i) {
res += t[i - 1];
}
return res;
}
long rangeSum(int l, int r){
return sum(r) - sum(l - 1);
}
}
public static void main(String[] args) throws IOException {
int t = f.nextInt();
while (t > 0) {
solve();
t--;
}
w.flush();
w.close();
br.close();
}
public static void solve() {
int n = f.nextInt();
int Q = f.nextInt();
int[] a = new int[n];
int[] pos = new int[n+1];
int[] dp = new int[n+1];
long[] res = new long[Q];
Fenwick F = new Fenwick(n);
for (int i = 0; i < n; i++) {
a[n - i - 1] = f.nextInt();
}
for (int i = 0; i < n; i++) {
pos[a[i]] = i;
}
ArrayList<int[]>[] q = new ArrayList[n];
for (int i = 0; i < n; i++) {
q[i] = new ArrayList<>();
}
for (int i = 0; i < Q; i++) {
int l = f.nextInt() - 1;
int r = f.nextInt() - 1;
{int tmp = l; l = r; r = tmp;}
l = n - l - 1;
r = n - r - 1; // 翻转数组后,查询区间会变化,所以讲区间修改为翻转后的区间
q[r].add(new int[] {l, i});
}
for (int r = 0; r < n; r++) { // 固定r,
int x = a[r];
dp[x] = 1;
for (int i = x; i <= n; i+=x) {
if (pos[i] > pos[x]) continue;
for (int j = 2 * i; j <= n; j+=i) {
if (pos[j] > pos[i]) continue;
dp[j] += dp[i];
}
}
for (int i = x; i <= n; i+=x) {
if (dp[i] == 0) continue;
F.add(pos[i], dp[i]); // 插入树状数组
dp[i] = 0;
}
for (int[] qr : q[r]) {
res[qr[1]] += F.rangeSum(qr[0], r);
}
}
for (int i = 0; i < Q; i++) {
w.print(res[i] + " ");
}
w.println();
}
static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
static Input f = new Input(System.in);
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static class Input {
public BufferedReader reader;
public StringTokenizer tokenizer;
public Input(InputStream stream) {
reader = new BufferedReader(new InputStreamReader(stream), 32768);
tokenizer = null;
}
public String next() {
while (tokenizer == null || !tokenizer.hasMoreTokens()) {
try {
tokenizer = new StringTokenizer(reader.readLine());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return tokenizer.nextToken();
}
public String nextLine() {
String str = null;
try {
str = reader.readLine();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return str;
}
public int nextInt() {
return Integer.parseInt(next());
}
public long nextLong() {
return Long.parseLong(next());
}
public Double nextDouble() {
return Double.parseDouble(next());
}
public BigInteger nextBigInteger() {
return new BigInteger(next());
}
}
}