HDU 4747 线段树 + 区间更新 + java

传送门 : HDU 4747

题外话

13年杭州网络赛的一题, 同学推荐做的, 算是磨练一下自己的线段树, 最近在系统的学java, 确实很骚气, 就用java写了, 搞事情啊
看来线段树还是有点成果的, 虽然慢了一点, 但是1A
有大神这题40行dp, 太可怕了, 附个链接:DP递推计数(ORZ)

题意

给定n个数找出Sum{mex(i, j)}
mex(i, j )指[i, j]区间未出现过得最小自然数值

分析

  1. 这个值是固定一个左端点i, 求mex(i, j)(1 <= j <= n)累加, 而且mex(i, j)是单调不减的
  2. 先求出固定1的mex累加, 然后依次删除左边的端点
  3. 对于一个a[i], 固定i查询mex累加和后, 更新到固定i + 1的状态需要先把[1, i]清零,然后在下一个a[i]出现位置之前, 如果有mex域大于a[i]的, 很显然这一段mex都将变为a[i], 且这个区间左边要是第一个mex域就大于a[i]的位置

题解

首先可以求得mex(1, j)(1 <= j <= n)然后存mex[i]建线段树
预处理每个数的下一个出现位置
查询固定以第i个数位左端点的mex(i, j)后, 删左端点, 用区间更新把[1, i]内的mex清零
然后如果根节点的mex域大于a[i],找到最左边mex域大于a[i]的位置l, 如果这个位置在a[i]下一个出现未知的左边, 把这段[l, r - 1]的mex域更新为a[i].
java实现

AC code:(java)

//package adruill;
import java.util.*;
import static java.lang.Math.*;
import java.io.*;


public class Main{

    final static int N = 200000 + 5;
    static int[] mex = new int[N];//预处理mex
    static int[] mx = new int[N << 2];//线段树mex域
    static long[] sum = new long[N << 2];//sum域
    static int[] lazy = new int[N << 2];//lazy
    static int[] a = new int[N];
    static int n;
    public static void main(String[] args) throws IOException{
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
            n = in.nextInt();
            if(n == 0) break;

            int[] vis = new int[N];
            int[] next = new int[N];

            int tmp = 0;
            for(int i = 1; i <= n; ++i){
                a[i] = in.nextInt();
                if(a[i] > n) a[i] = n + 1;//对于大于n的都看作n + 1
                vis[a[i]] = n + 1;
                while(vis[tmp] != 0)  ++tmp;//预处理mex
                mex[i] = tmp;
            }

            for(int i = n; i > 0; --i){
                next[i] = vis[a[i]];//逆向找a[i]的下一个出现位置
                //ot("" + next[i]);
                vis[a[i]] = i;
            }

            build(1, 1, n);//建树
            long ans = 0;
            for(int i = 1; i <= n; ++i){
                ans += sum[1];
                update(1, 1, n, 1, i, 0);
                if(a[i] < mx[1]){//当前根mex域判断有没有l存在
                    int r = next[i];
                    int l = GetPos(1, 1, n, a[i]);//l
                    if(r > l)
                        update(1, 1, n, l, r - 1, a[i]);//这段区间更新
                }
            }
            ot("" + ans);
        }
        in.close();
    }


    private static int GetPos(int rt, int l, int r, int v) {
        if(l == r)    return l;
        int mid = (l + r) >> 1;
        pushDown(rt, l, r);
        if(mx[lch(rt)] > v)    return GetPos(lch(rt), l, mid, v);//找最左边的位置
        return GetPos(rch(rt), mid + 1, r, v);
    }


    private static void update(int rt, int l , int r, int ul, int ur, int v) {
        if(ul <= l && ur >= r){
            lazy[rt] = 1;
            mx[rt] = v;
            sum[rt] = (long)(r - l + 1) * v ;
            return;
        }
        pushDown(rt, l, r);
        int mid = (l + r) >> 1;
        if(ul <= mid)    update(lch(rt), l, mid, ul, ur, v);
        if(ur > mid)    update(rch(rt), mid + 1, r, ul, ur, v);
        pushUp(rt);
    }


    private static void pushDown(int rt, int l, int r) {
        if(lazy[rt] != 0){
            int mid = (l + r) >> 1;
            down(lch(rt), rt, mid - l + 1);
            down(rch(rt), rt, r - mid);
            lazy[rt] = 0;
        }

    }


    private static void down(int s, int f, int len) {
        lazy[s] = lazy[f];
        mx[s] = mx[f];
        sum[s] = (long)len * mx[f];
    }


    public static void build(int rt, int l, int r){
        lazy[rt] = 0;
        if(l == r){
            sum[rt] = mex[l];
            mx[rt] = mex[l];
            return;
        }

        int mid = (l + r) >> 1;
        build(rt << 1, l, mid);
        build(rt << 1 | 1, mid + 1, r);
        pushUp(rt);
    }


    private static void pushUp(int rt) {
        sum[rt] = sum[lch(rt)] + sum[rch(rt)];
        mx[rt] = max(mx[lch(rt)], mx[rch(rt)]);
    }


    private static int rch(int rt) {
        return lch(rt) | 1;
    }


    private static int lch(int rt) {
        return rt << 1;
    }


    public static void ot(String s){
        System.out.println(s);
        //System.out.printf("%1$s %2$tB %2$td, %2$tY", "Due date : ", new Date());
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值