2024牛客寒假集训营4 -- H数三角形(hard) 题解

文章讲述了如何解决一个关于H数三角形的问题,通过观察图形特性,利用树状数组优化时间复杂度,从O(N^3)降低到更高效的方法。关键在于识别对称性并统计连续底面上的合法数字三角形区域。
摘要由CSDN通过智能技术生成

目录

H数三角形(hard)

题目大意:

思路解析:

​编辑 

 代码实现:


H数三角形(hard)

题目大意:

        

思路解析:

         

通过这张图可以发现,左腰和右腰是对称的,那么只要对于 (i,j)这个点,如果有一个端点能通过左腰和右腰能够到达这个点那么他就能组成一个数字三角形,但是我们如果遍历找是否有这样的点时间复杂度就会变为N^3, (数字三角形要求整个底面是连续的,所以我们在连续底面统计每一个点能通过左腰和右腰达到最右边的点(这里统计可以用树状数组维护))我们统计这个点通过右腰和左腰能到达最左的点,那么这个区间之内就是合法的数字三角形。

 

 代码实现:

        

import java.io.*;
import java.util.*;

public class Main {
    static int mod = (int) 1e9 + 7;
    static int mod9 = 998244353;
    static int N = 3003;
    static int[] tr;
    static Vector<Integer> g[];
    static int[][] d1; // 左腰
    static int[][] d2; // 右腰
    static int n;
    static int m;
    static long ans = 0;

    public static void main(String[] args) throws IOException {
        n = input.nextInt();
        m = input.nextInt();
        tr = new int[N];
        char[][] map = new char[N][N];
        g = new Vector[N * 2];
        for (int i = 0; i < n * 2 + 6; i++) {
            g[i] = new Vector<>();
        }
        d1 = new int[N][N];
        d2 = new int[N][N];
        for (int i = 1; i <= n; i++) {
            char[] s = input.next().toCharArray();
            for (int j = 1; j <= m; j++) {
                map[i][j] = s[j - 1];
                if (map[i][j] == '*'){
                    ans --;
                    d1[i][j] = d1[i-1][j+1] + 1;
                    d2[i][j] = d2[i-1][j-1] + 1;
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (map[i][j] != '*') continue;
                int k = j;
                while (k + 1<=m && map[i][k+1] == '*') k++;
                solve(i, j, k);
                j = k;
            }
        }
        out.println(ans);
        out.flush();
        out.close();
        br.close();
    }
    public static void add(int x, int val){
        for (; x <= m; x += x & -x) {
            tr[x] += val;
        }
    }
    public static int sum(int x){
        if (x <= 0) return 0;
        int res = 0;
        for (; x > 0; x -= x &-x) res+=tr[x];
        return res;
    }
    public static void solve(int row, int left, int right){
        int mx = 0;
        int i;
        for (i = left; i <= right ; i+=2) {
            int L = i;
            int R = L + d1[row][i] * 2 - 2;
            mx = Math.max(mx, R);
            g[R].add(i);
            add(i, 1);
            R = i;
            L = R - d2[row][i] * 2 + 2;
            ans += sum(i) - sum(L - 1);
            for (Integer integer : g[i]) {
                add(integer, -1);
            }
            g[i].clear();
        }
        for (; i <= mx; i+=2) {
            for (Integer integer : g[i]) {
                add(integer, -1);
            }
            g[i].clear();
        }
        mx = 0;
        for (i = left + 1; i<=right; i+=2) {
            int L = i;
            int R = L + d1[row][i] * 2 - 2;
            mx = Math.max(mx, R);
            g[R].add(i);
            add(i, 1);
            R = i;
            L = R - 2 * d2[row][i] + 2;
            ans += sum(i) - sum(L - 1);
            for (Integer integer :g[i]) {
                add(integer,-1);
            }
            g[i].clear();
        }
        for(; i<=mx;i+=2){
            for (Integer integer :g[i]) {
                add(integer,-1);
            }
            g[i].clear();
        }
    }

    // -------------------------------- 模板 ---------------------------
    public static long gcd(long a, long b) {
        if (a == 0) return b;
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    public static int gcd(int a, int b) {
        if (a == 0) return b;
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    public static long pow(long a, long b, long mod) {
        long res = 1;
        while (b > 0) {
            if ((b & 1) == 1) res = (res * a) % mod;
            a = (a * a) % mod;
            b >>= 1;
        }
        return res;
    }

    //


    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static Input input = 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 int nextInt() {
            return Integer.parseInt(next());
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Studying~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值