LA 3294 The Ultimate Bamboo Eater (线段树套Treap)

题意: 有一只熊猫和n个岛,岛分布在二维平面上,坐标为Xi,Yi,岛上有竹子,岛i上的竹子数量为Li,美味值为Wi,每个岛的Wi值唯一。熊猫每天要吃掉一个岛的竹子,并且当天所在的岛上的竹子要比之前所在的岛上的竹子都要美味。熊猫沿着曼哈顿距离在岛之间移动。如果熊猫从岛A移动到岛B走的距离dis>岛B上的竹子的数量,那么熊猫会伤心而死(不吃也是要挂的)。熊猫的初始位置由你选择,问熊猫最多能活几天。存在坐标相同的岛屿。

解法: 把岛屿按W值排序,用类似于求最长上升序列的方法做,设f[i]为位置在岛i时的最长存活天数,那么有f[i] = max(f[j])+1,(dis(i,j)<=L[i])。对于曼哈顿距离的处理,因为到一个点曼哈顿距离小于等于k的区域是一个斜置45度的正方形,那么我们可以通过旋转坐标系使正方形正置,新的坐标为(x`,y`) = (x,y)*(1,1)+(x,y)*(-1,1) = (x-y,x+y)。然后的任务就是写一个数据结构支持查询平面内任意一块正方形区域的最大值,单点插入,那么就是树套树了。由于我第一维选用的线段树,需要对x离散化。

/* Created Time: Monday, October 28, 2013 PM08:08:09 CST */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int M = 100010;
int n,y1,y2;
struct point {
    int x,y,w,c;
    void read() {
        int a,b;
        scanf("%d%d%d%d",&a,&b,&w,&c);
        x = a-b;
        y = a+b;
    }
    bool operator < (const point &tt) const {
        return w<tt.w;
    }
}dot[M];
struct San {
    int a[M],n;
    void clear() { n = 0;}
    void push(int x) { a[n++] = x; }
    void doit() { sort(a,a+n); n = unique(a,a+n)-a; }
    int operator [] (int x) { return lower_bound(a,a+n,x)-a; }
    int size() { return n; }
}sx;
struct Node {
    Node *ch[2];
    int y,v,mv,r,size;
}memo[M*20],*t[M<<2],*nill;
int tot;
void up(Node *o) {
    if (o==nill) return ;
    o->mv = max(o->v,max(o->ch[0]->mv,o->ch[1]->mv));
    o->size = 1 + o->ch[0]->size + o->ch[1]->size;
}
int ran() { static int ranx = 987456321; return ranx += (ranx<<2)+1; }
void New_node(Node *&o,int y,int v) {
    o = &memo[tot++];
    o->size = 1;
    o->ch[0] = o->ch[1] = nill;
    o->v = o->mv = v;
    o->y = y;
    o->r = ran();
}
// 把小于等于y的部分切到a
void cut(Node *o,Node *&a,Node *&b,int y) {
    if (o==nill) {
        a = b = nill;
    } else if (o->y > y) {
        b = o;
        cut(o->ch[0],a,b->ch[0],y);
        up(b);
    } else {
        a = o;
        cut(o->ch[1],a->ch[1],b,y);
        up(a);
    }
}
void merge(Node *&o,Node *a,Node *b) {
    if (a==nill || b==nill) {
        o = (a==nill?b:a);
    } else if (a->r > b->r) {
        o = a;
        merge(o->ch[1],a->ch[1],b);
    } else {
        o = b;
        merge(o->ch[0],a,b->ch[0]);
    }
    up(o);
}
void insert(Node *&o,int y,int v) {
    Node *a,*b,*c;
    cut(o,a,b,y);
    New_node(c,y,v);
    merge(a,a,c);
    merge(o,a,b);
}
int query(Node *&o) {
    Node *a,*b,*c;
    cut(o,a,b,y1-1);
    cut(b,b,c,y2);
    int ret = b->mv;
    merge(a,a,b);
    merge(o,a,c);
    return ret;
}
void build(int l,int r,int rt) {
    t[rt] = nill;
    if (l==r) return ;
    int mid = l+r>>1;
    build(lson);
    build(rson);
}
void update(int x,int y,int v,int l,int r,int rt) {
    insert(t[rt],y,v);
    if (l==r) return ;
    int mid = l+r>>1;
    if (x<=mid) 
        update(x,y,v,lson);
    else 
        update(x,y,v,rson);
}
int query(int L,int R,int l,int r,int rt) {
    if (L<=l && r<=R) 
        return query(t[rt]);
    int mid = l+r>>1;
    int ret = 0;
    if (L<=mid) 
        ret = max(ret,query(L,R,lson));
    if (R> mid)
        ret = max(ret,query(L,R,rson));
    return ret;
}
int work() {
    int ret = 1;
    sx.doit();
    sort(dot,dot+n);
    tot = 0;
    New_node(nill,0,0);
    nill->size = 0;
    build(0,sx.size()-1,1);
    for (int i = 0; i < n; i ++) {
        int c = dot[i].c;
        y1 = dot[i].y-c;
        y2 = dot[i].y+c;
        int x1 = sx[dot[i].x-c];
        int x2 = sx[dot[i].x+c];
        if (dot[i].x-c>sx.a[x1]) x1 ++;
        int v = query(x1,x2,0,sx.size()-1,1)+1;
        if (ret<v) ret = v;
        update(sx[dot[i].x],dot[i].y,v,0,sx.size()-1,1);
    }
    return ret;
}
int main() {
    int cas,ca=0;
    scanf("%d",&cas);
    while (cas--) {
        scanf("%d",&n);
        sx.clear();
        for (int i = 0; i < n; i ++) {
            dot[i].read();
            sx.push(dot[i].x);
        }
        printf("Case %d: %d\n",++ca,work());
    }
    return 0;
}


可以使用Java中的多线程来实现这个场景。首先定义一个桌子类,其中包含一个汉堡数量的变量和对应的生产和消费方法。然后定义一个厨师类和一个吃货类,分别继承自线程类,实现对桌子的生产和消费操作。具体实现代码如下: ```java class Table { private int burgerNum = 0; public synchronized void produce() { while (burgerNum >= 10) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } burgerNum++; System.out.println("厨师生产了一个汉堡,当前桌子上有" + burgerNum + "个汉堡"); notifyAll(); } public synchronized void consume() { while (burgerNum <= 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } burgerNum--; System.out.println("吃货吃了一个汉堡,当前桌子上有" + burgerNum + "个汉堡"); notifyAll(); } } class Chef extends Thread { private Table table; public Chef(Table table) { this.table = table; } @Override public void run() { while (true) { table.produce(); } } } class Eater extends Thread { private Table table; public Eater(Table table) { this.table = table; } @Override public void run() { while (true) { table.consume(); } } } public class Main { public static void main(String[] args) { Table table = new Table(); Chef chef = new Chef(table); Eater eater = new Eater(table); chef.start(); eater.start(); } } ``` 在这个实现中,桌子类中的生产和消费方法都使用了synchronized关键字来保证线程安全。厨师和吃货类分别继承自线程类,并在run方法中不断地调用桌子类的生产和消费方法。在主函数中创建桌子、厨师和吃货对象,并启动线程。这样就可以实现一个简单的生产者消费者模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值