team2012-F3-sol-0024

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
题意:
给定一些玩具用坐标x和高度h表示,如果向左或向右推倒一个玩具(x,h),那么在(x-h, x)或(x, x+h)的范围内的玩具均被推倒
这样会形成骨牌效应,问最少几次能推倒所有玩具。

解法:
首先要处理出每个玩具向左和向右最远能推到几号玩具lft[i]和rgt[i]
则lft[i] = min(lft[j] | x-h <= j < x), rgt[i] = max(rgt[j] | x < j <= x+h)
需要用线段树优化这个处理过程,然后设f[i]为推倒前i个玩具的最小次数,则

1) 若最后一块是向左倒的,有 f[i] = min(f[j]+1 | lft[i]-1 <= j < i)
2) 若最后一块是向右倒的,有 f[i] = min(f[j-1]+1 | rgt[j] >= i)

同样能够用线段树优化这个DP过程,总复杂度 O(nlogn)
}}}
{{{
#include <iostream>
#include <cstdio>
#include <utility>
#include <map>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
template<class T> bool takemin(T& a, T b) {bool Z=(a>b); if(Z)a=b; return Z;}
template<class T> bool takemax(T& a, T b) {bool Z=(a<b); if(Z)a=b; return Z;}

const int MAXN = 65536*4 + 100;
const int GETMIN = 0, GETMAX = 1, INFI = 2100000000;
int l[MAXN], r[MAXN], minn[MAXN], maxx[MAXN], lch[MAXN], rch[MAXN], mid[MAXN];
int n, flag, lft[MAXN], rgt[MAXN], f[MAXN], min2[MAXN];
PII toy[MAXN];
map<int, int> num;

void build(int rt, int ll, int rr)
{
    l[rt] = ll;
    r[rt] = rr;
    if(ll == rr)
    {
        minn[rt] = maxx[rt] = ll;
        min2[rt] = INFI;
        return;
    }
    lch[rt] = (rt << 1);
    rch[rt] = lch[rt] + 1;
    mid[rt] = (ll + rr) / 2;
    build(lch[rt], ll, mid[rt]);
    build(rch[rt], mid[rt] + 1, rr);
    minn[rt] = min(minn[lch[rt]], minn[rch[rt]]);
    maxx[rt] = max(maxx[lch[rt]], maxx[rch[rt]]);
    min2[rt] = INFI;
}

void update(int rt, int w, int v)
{
    if(l[rt] == r[rt])
    {
        if(flag == GETMIN)   minn[rt] = v;
        if(flag == GETMAX)   maxx[rt] = v;
        return;
    }
    if(w <= mid[rt])
        update(lch[rt], w, v);
    else
        update(rch[rt], w, v);
    maxx[rt] = max(maxx[lch[rt]], maxx[rch[rt]]);
    minn[rt] = min(minn[lch[rt]], minn[rch[rt]]);
}

int query(int rt, int ll, int rr)
{
    if(ll == l[rt] && rr == r[rt])
    {
        if(flag == GETMIN)
            return minn[rt];
        if(flag == GETMAX)
            return maxx[rt];
    }
    if(rr <= mid[rt])
        return query(lch[rt], ll, rr);
    if(ll > mid[rt])
        return query(rch[rt], ll, rr);

    int ret = query(lch[rt], ll, mid[rt]);
    if(flag == GETMAX)
        takemax(ret, query(rch[rt], mid[rt]+1, rr));
    if(flag == GETMIN)
        takemin(ret, query(rch[rt], mid[rt]+1, rr));
    return ret;
}

void update2(int rt, int ll, int rr, int v)
{
    if(ll == l[rt] && rr == r[rt])
    {
        takemin(min2[rt], v);
        return;
    }
    if(rr <= mid[rt])
        update2(lch[rt], ll, rr, v);
    else if(ll > mid[rt])
        update2(rch[rt], ll, rr, v);
    else
    {
        update2(lch[rt], ll, mid[rt], v);
        update2(rch[rt], mid[rt]+1, rr, v);
    }
}

int query2(int rt, int w)
{
    if(l[rt] == w && r[rt] == w)
        return min2[rt];
    if(w <= mid[rt])
        return min(query2(lch[rt], w), min2[rt]);
    if(w > mid[rt])
        return min(query2(rch[rt], w), min2[rt]);
}

void init_left()
{
    flag = GETMIN;
    for(int i = 1; i <= n; i++)
    {
        int x = toy[i].first, h = toy[i].second;
        PII tmp = make_pair(x-h, -1);
        int loc = lower_bound(toy+1, toy+n+1, tmp) - toy;
        lft[i] = query(1, loc, i);
        update(1, i, lft[i]);
    }
}

void init_right()
{
    flag = GETMAX;
    for(int i = n; i >= 1; i--)
    {
        int x = toy[i].first, h = toy[i].second;
        PII tmp = make_pair(x+h, INFI);
        int loc = lower_bound(toy+1, toy+n+1, tmp) - toy - 1;
        rgt[i] = query(1, i, loc);
        update(1, i, rgt[i]);
    }
}

void gao()
{
    f[0] = 0;
    flag = GETMIN;
    for(int i=1;i<=n;i++)
    {
        f[i] = query2(1, i);
        takemin(f[i], f[lft[i]-1] + 1);
        if(lft[i] != i)
            takemin(f[i], query(1, lft[i], i-1) + 1);
        update(1, i, f[i]);
        if(rgt[i] != i)
            update2(1, i+1, rgt[i], f[i-1]+1);
    }
}

int main()
{
    while(~scanf("%d", &n))
    {
        int tot = 0;
        num.clear();
        for(int i = 1; i <= n; i++)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            PII tmp = make_pair(a, b);
            if(num[a] == 0)
            {
                toy[++tot] = tmp;
                num[a] = tot;
            }
            else
                takemax(toy[num[a]], tmp);
        }
        n = tot;
        sort(toy+1, toy+n+1);

        build(1, 1, n);
        init_left();
        init_right();
        gao();

        printf("%d\n", f[n]);
    }
    return 0;
}
}}}
题意:
给定一些玩具用坐标x和高度h表示,如果向左或向右推倒一个玩具(x,h),那么在(x-h, x)或(x, x+h)的范围内的玩具均被推倒
这样会形成骨牌效应,问最少几次能推倒所有玩具。
解法:
首先要处理出每个玩具向左和向右最远能推到几号玩具lft[i]和rgt[i]
则lft[i] = min(lft[j] | x-h <= j < x), rgt[i] = max(rgt[j] | x < j <= x+h)
需要用线段树优化这个处理过程,然后设f[i]为推倒前i个玩具的最小次数,则
1) 若最后一块是向左倒的,有 f[i] = min(f[j]+1 | lft[i]-1 <= j < i)
2) 若最后一块是向右倒的,有 f[i] = min(f[j-1]+1 | rgt[j] >= i)
同样能够用线段树优化这个DP过程,总复杂度 O(nlogn)
#include <iostream>
#include <cstdio>
#include <utility>
#include <map>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
template<class T> bool takemin(T& a, T b) {bool Z=(a>b); if(Z)a=b; return Z;}
template<class T> bool takemax(T& a, T b) {bool Z=(a<b); if(Z)a=b; return Z;}
const int MAXN = 65536*4 + 100;
const int GETMIN = 0, GETMAX = 1, INFI = 2100000000;
int l[MAXN], r[MAXN], minn[MAXN], maxx[MAXN], lch[MAXN], rch[MAXN], mid[MAXN];
int n, flag, lft[MAXN], rgt[MAXN], f[MAXN], min2[MAXN];
PII toy[MAXN];
map<int, int> num;
void build(int rt, int ll, int rr)
{
    l[rt] = ll;
    r[rt] = rr;
    if(ll == rr)
    {
        minn[rt] = maxx[rt] = ll;
        min2[rt] = INFI;
        return;
    }
    lch[rt] = (rt << 1);
    rch[rt] = lch[rt] + 1;
    mid[rt] = (ll + rr) / 2;
    build(lch[rt], ll, mid[rt]);
    build(rch[rt], mid[rt] + 1, rr);
    minn[rt] = min(minn[lch[rt]], minn[rch[rt]]);
    maxx[rt] = max(maxx[lch[rt]], maxx[rch[rt]]);
    min2[rt] = INFI;
}
void update(int rt, int w, int v)
{
    if(l[rt] == r[rt])
    {
        if(flag == GETMIN)   minn[rt] = v;
        if(flag == GETMAX)   maxx[rt] = v;
        return;
    }
    if(w <= mid[rt])
        update(lch[rt], w, v);
    else
        update(rch[rt], w, v);
    maxx[rt] = max(maxx[lch[rt]], maxx[rch[rt]]);
    minn[rt] = min(minn[lch[rt]], minn[rch[rt]]);
}
int query(int rt, int ll, int rr)
{
    if(ll == l[rt] && rr == r[rt])
    {
        if(flag == GETMIN)
            return minn[rt];
        if(flag == GETMAX)
            return maxx[rt];
    }
    if(rr <= mid[rt])
        return query(lch[rt], ll, rr);
    if(ll > mid[rt])
        return query(rch[rt], ll, rr);
    int ret = query(lch[rt], ll, mid[rt]);
    if(flag == GETMAX)
        takemax(ret, query(rch[rt], mid[rt]+1, rr));
    if(flag == GETMIN)
        takemin(ret, query(rch[rt], mid[rt]+1, rr));
    return ret;
}
void update2(int rt, int ll, int rr, int v)
{
    if(ll == l[rt] && rr == r[rt])
    {
        takemin(min2[rt], v);
        return;
    }
    if(rr <= mid[rt])
        update2(lch[rt], ll, rr, v);
    else if(ll > mid[rt])
        update2(rch[rt], ll, rr, v);
    else
    {
        update2(lch[rt], ll, mid[rt], v);
        update2(rch[rt], mid[rt]+1, rr, v);
    }
}
int query2(int rt, int w)
{
    if(l[rt] == w && r[rt] == w)
        return min2[rt];
    if(w <= mid[rt])
        return min(query2(lch[rt], w), min2[rt]);
    if(w > mid[rt])
        return min(query2(rch[rt], w), min2[rt]);
}
void init_left()
{
    flag = GETMIN;
    for(int i = 1; i <= n; i++)
    {
        int x = toy[i].first, h = toy[i].second;
        PII tmp = make_pair(x-h, -1);
        int loc = lower_bound(toy+1, toy+n+1, tmp) - toy;
        lft[i] = query(1, loc, i);
        update(1, i, lft[i]);
    }
}
void init_right()
{
    flag = GETMAX;
    for(int i = n; i >= 1; i--)
    {
        int x = toy[i].first, h = toy[i].second;
        PII tmp = make_pair(x+h, INFI);
        int loc = lower_bound(toy+1, toy+n+1, tmp) - toy - 1;
        rgt[i] = query(1, i, loc);
        update(1, i, rgt[i]);
    }
}
void gao()
{
    f[0] = 0;
    flag = GETMIN;
    for(int i=1;i<=n;i++)
    {
        f[i] = query2(1, i);
        takemin(f[i], f[lft[i]-1] + 1);
        if(lft[i] != i)
            takemin(f[i], query(1, lft[i], i-1) + 1);
        update(1, i, f[i]);
        if(rgt[i] != i)
            update2(1, i+1, rgt[i], f[i-1]+1);
    }
}
int main()
{
    while(~scanf("%d", &n))
    {
        int tot = 0;
        num.clear();
        for(int i = 1; i <= n; i++)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            PII tmp = make_pair(a, b);
            if(num[a] == 0)
            {
                toy[++tot] = tmp;
                num[a] = tot;
            }
            else
                takemax(toy[num[a]], tmp);
        }
        n = tot;
        sort(toy+1, toy+n+1);
        build(1, 1, n);
        init_left();
        init_right();
        gao();
        printf("%d\n", f[n]);
    }
    return 0;
}