Light OJ 1402 (Dp + 树状数组)

题目:

[LINK](http://www.lightoj.com/volume_showproblem.php?problem=1402)
给你一些点(SRT, R);
按照SRT排序, 然后再坐标系中画出这些点, 连成一条线。
线会有峰点(peak),一个点是峰点, 相邻两个点的 R 值都比它小 或 大。
然后WARush 要丧病的忽略一些点,(忽略第X点后,第 X 后面的点会前移! ) 
会形成新的线。 然后题目要求输出所有线条中刚好有 K 个峰点的**不同线条**的数量。

分析:

题目中 单个点 和 没有点 都算一种情况。
先把所有点排序, 然后依次画图, 统计线条数。
对于 第 i 个点画图时 :
    枚举 K 值
    当K == 0时, 我们可以得出:
        上升线条 Up = [比 Ri 低的点的数目] + [总结点数] + [上升线条尾端点值比 Ri 低的数]
        下降线条 Down 同理。
        故Dp[k] = Up + Down + 节点node 数。
    当 K > 0 时。 :
        上升线条 Up(k) = [上升线条 Up(k) 中尾端点比 Ri 小的线条数] + \
                       [ 下降线条 Down(k-1) 中尾端点比 Ri 小的线条数].
        下降线条 Down(k) 同理。
怎么求这些值? 用树状数组维护就好了。

Code:

#include <bits/stdc++.h>

using namespace std;

typedef unsigned int U32;
const int maxn = 10000 + 131;
inline int lowbit(int x) {
    return x & (-x);
}
///树状数组
struct BIT {
    U32 Num[maxn];
    int N;

    void INIT(int n) {
        N = n;
        memset(Num, 0, sizeof(Num));
    }

    void Add(int x, U32 val) {
        while(x <= N) {
            Num[x] += val;
            x   += lowbit(x);
        }
    }

    U32 Sum(int x) {
        U32 ret = 0;
        while(x > 0) {
            ret += Num[x];
            x   -= lowbit(x);
        }
        return ret;
    }

    U32 Sec(int L, int R) {
        return Sum(R) - Sum(L-1);
    }
};
/// DP 状态
struct State {
    BIT Up, Down, Node;
    void INIT(int n) {
        Up.INIT(n);
        Down.INIT(n);
        Node.INIT(n);
    }
};

struct Data {
    int s, r;
    bool operator < (const Data& a) const {
        return this->s < a.s;
    }
};

Data node[maxn];
State Dp[60];
int R_n[maxn];

int main() {
    int T;
    scanf("%d",&T);
    for(int kase = 1; kase <= T; ++kase) {
        int n, K;
        scanf("%d%d",&n, &K);
        for(int i = 0; i < n; ++i) {
            scanf("%d%d",&node[i].s, &node[i].r);
            R_n[i] = node[i].r;
        }
        sort(R_n, R_n+n);
        int C   = unique(R_n, R_n+n) - R_n;
        int Max = -1;
        for(int i = 0; i < n; ++i) {
            node[i].r = lower_bound(R_n, R_n+C, node[i].r)-R_n+1;
            Max       = max(Max, node[i].r);
        }
        sort(node, node+n);
        ///离散化结束
        for(int i = 0; i <= K; ++i) Dp[i].INIT(Max);
        for(int i = 0; i < n; ++i) {
            int h = node[i].r;
            for(int k = 0; k <= K; ++k) {
                //相同点会造成相同的线条, 所以要减去。
                U32 Sum_Up   = -Dp[k].Up.Sec(h,h);
                U32 Sum_down = -Dp[k].Down.Sec(h,h);
                U32 Sum_node = -Dp[k].Node.Sec(h,h);
                Sum_Up       += Dp[k].Up.Sum(h-1) + Dp[k].Node.Sum(h-1);
                Sum_down     += Dp[k].Down.Sec(h+1, Max) + Dp[k].Node.Sec(h+1,Max);
                if(k == 0) Sum_node++;
                else {
                    Sum_Up   += Dp[k-1].Down.Sum(h-1);
                    Sum_down += Dp[k-1].Up.Sec(h+1, Max);
                }
                if(Sum_Up) Dp[k].Up.Add(h, Sum_Up);
                if(Sum_down) Dp[k].Down.Add(h, Sum_down);
                if(Sum_node) Dp[k].Node.Add(h, Sum_node);
            }
        }
        U32 Ans = 0;
        Ans = Dp[K].Down.Sum(Max) + Dp[K].Up.Sum(Max) + Dp[K].Node.Sum(Max);
        if(K == 0) Ans ++;
        printf("Case %d: %u\n",kase, Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值