Codeforces Round 936 (Div. 2) ---- F. Nobody is needed ---- 题解

目录

F. Nobody is needed:

题目大意:

 思路解析:

代码实现 :(我用的翻转数组的方法)


 

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());
        }
    }
}

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值