HDU 4742 Pinball Game 3D (树套树)

针对三维空间中的点集,利用动态规划与数据结构求解最长hit序列及其计数方法。通过对点进行排序,并借助树状数组与Treap,实现高效的解算。

非正解,卡时限AC,慎入- -!

题意: 三维空间里有10W个点,点a可以'hit'点b当且仅当xa<=xb&&ya<=yb&&za<=zb。现在要你选出一部分点来构成这样一个满足hit关系的序列,问最长序列的长度,以及构成这个序列的方法数。

解法: 用dp[i]表示以点i结尾的最长序列长度,cnt[i]表示能取到当前dp值下的方法数,那么有

dp[i] = max{dp[j] | j 'hit' i} + 1,

cnt[i] = sigma{cnt[j] | dp[j] = dp[i]-1 && j 'hit' i}

方程是显然的,而问题的关键则在于方程的转移,在这个数据范围下,暴力O(n)转移是不可行的,那么就用些数据结构来搞定它。

对于一维下的这个问题,我们只需要通过一次排序就可以轻松解决。

对于二维下的这个问题,排序后用一个树状数组,依旧轻松。

那么当问题拓展到三维时,用排序解决x轴,用树状数组解决y轴,还剩个z轴呢?只需要套个treap就ok了。

先对所有点排序,保证在转移时能'hit'当前点的所有点都已经被计算完毕,这样x轴就可以在计算中忽略不计了。对于一个点p(xp,yp,zp),我们再用树状数组来过滤掉所有y坐标大于yp的点,最后,随便找一个二叉树来过滤掉所有z坐标不符合的点,dp[i]和cnt[i]这两个值直接维护在Treap上就可以了。

写得很搓的代码,跑了4640MS华丽丽的垫了个底。其实第一发是超时的,但是在加了几个inline后就成功卡过去了,笔者表示灰常蛋疼。

优化空间其实蛮大的,目测可以跑进3S或者2S的样子。

传说正解是分治

/* Created Time: 2013年09月18日 星期三 22时31分14秒 */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int MOD = 1<<30;
typedef pair<int,int> PP;
int n;
inline void tomax(PP &a, PP b) {
    if (a.first < b.first) a = b;
    else if (a.first == b.first) {
        a.second += b.second;
        if (a.second >= MOD) a.second -= MOD;
    }
}
struct San {
    int a[MAXN],n;
    void init() { n = 0; }
    void push(int x) { a[++n] = x; }
    void doit() {
        sort(a+1, a+n+1);
        n = unique(a+1,a+n+1) - a - 1;
    }
    int size() { return n; }
    int operator [] (int x) { return lower_bound(a+1,a+n+1,x) - a; }
}sy;
struct point {
    int x,y,z;
    void read() { scanf("%d%d%d", &x, &y, &z); }
    bool operator < (const point & tt) const {
        if (x == tt.x) {
            if (y == tt.y) return z < tt.z;
            return y < tt.y;
        }
        return x < tt.x;
    }
}dot[MAXN];
struct Node {
    Node *ch[2];
    int zval,r;
    PP hit,tms;
    void up() {
        tms = hit;
        for (int i = 0; i < 2; i ++) 
            tomax(tms, ch[i]->tms);
    }
    int cmp(int x) {
        if(x == zval) return -1;
        return x < zval ? 0 : 1;
    }
} *list, *head[MAXN], *nill;
inline int ran() {
    static int ranx = 123654987;
    ranx += (ranx<<2) + 1;
    return ranx;
}
inline void New_node(Node *&o, int zval, PP hah) {
    if (list == NULL) {
        Node *tt = new Node[10000];
        for (int i = 0; i < 10000; i ++) {
            tt[i].ch[1] = list;
            tt[i].r = ran();
            list = tt+i;
        }
    }
    o = list;
    list = list->ch[1];
    o->ch[0] = o->ch[1] = nill;
    o->zval = zval;
    o->tms = o->hit = hah;
}
void reuse(Node *o) {
    if (o == nill) return ;
    reuse(o->ch[0]);
    reuse(o->ch[1]);
    o->ch[1] = list;
    list = o;
}
inline void rotato(Node *&o, int d) {
    Node *temp = o;
    o = o->ch[d^1];
    temp->ch[d^1] = o->ch[d];
    o->ch[d] = temp;
    o->ch[d]->up();
    o->up();
}
void insert(Node *&o, int zval, PP hah) {
    if (o == nill) {
        New_node(o, zval, hah);
        return ;
    }
    int d = o->cmp(zval);
    if (d == -1) {
        tomax(o->hit, hah);
        o->up();
    } else {
        insert(o->ch[d], zval, hah);
        o->up();
        if (o->ch[d]->r > o->r) rotato(o,d^1);
    }
}
PP query(Node *o, int zval) {
    if (o == nill) return PP(-1,0);
    int d = o->cmp(zval);
    if (d == 0) return query(o->ch[0], zval);
    else {
        PP ret = o->hit;
        tomax(ret, o->ch[0]->tms);
        if (d == 1) {
            PP tt = query(o->ch[1], zval);
            tomax(ret, tt);
        }
        return ret;
    }
}
inline void add(int pos, int zval, PP hah) {
    for ( ; pos <= sy.size(); pos += pos&-pos) {
        insert(head[pos], zval, hah);
    }
}
PP sum(int pos, int zval) {
    PP ret = PP(0,0);
    for ( ; pos > 0; pos -= pos&-pos) {
        PP tt = query(head[pos], zval);
        tomax(ret, tt);
    }
    return ret;
}
void work() {
    PP ans = PP(0,1);
    sort(dot,dot+n);
    for (int i = 1; i <= n; i ++) head[i] = nill;
    for (int i = 0; i < n; i ++) {
        int yy = sy[dot[i].y];
        PP haha = PP(0,1);
        tomax(haha, sum(yy, dot[i].z));
        haha.first ++;
        tomax(ans, haha);
        add(yy, dot[i].z, haha);
    }
    printf("%d %d\n", ans.first, ans.second);
}
int main() {
    int cas;
 //   freopen("date.txt", "r", stdin);
    New_node(nill,-1,PP(0,0));
    scanf("%d", &cas);
    while (cas --) {
        scanf("%d", &n);
        sy.init();
        for (int i = 0; i < n; i ++) {
            dot[i].read();
            sy.push(dot[i].y);
        }
        sy.doit();
        work();
        for (int i = 1; i <= n; i ++) reuse(head[i]);
    }
    return 0;
}

重敲静态内存池3.5S

/* Created Time: 2013年09月19日 星期四 18时10分10秒 */
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef pair<int,int> PII;
const int MAXN = 100010;
const int MOD = 1<<30;
int n;
void tomax(PII &a, PII b) {
    if (a.first < b.first) a = b;
    else if (a.first == b.first) {
        a.second += b.second;
        if (a.second >= MOD)
            a.second -= MOD;
    }
}
struct point {
    int x,y,z;
    void read() { scanf("%d%d%d", &x, &y, &z); }
    bool operator < (const point &tt) const {
        if (x == tt.x) {
            if (y == tt.y) return z < tt.z;
            return y < tt.y;
        }
        return x < tt.x;
    }
}dot[MAXN];
struct San {
    int a[MAXN], n;
    void init() { n = 0; }
    void push(int x) { a[n++] = x; }
    void doit() { sort(a,a+n); n = unique(a,a+n) - a; }
    int size() { return n; }
    int operator [] (int x) { return lower_bound(a,a+n,x)-a+1; }
}sy;
struct Node {
    Node *ch[2];
    int v,r;
    PII hit,tms;
    int cmp(int x) { return x == v ? -1 : (x > v); }
} *head[MAXN], *nill, memo[MAXN*9];
int tot, val;
PII haha;
int ran() {
    static int ranx = 654321789;
    ranx += (ranx<<2) + 1;
    return ranx;
}
void New_node(Node *&o, int val, PII haha) {
    o = &memo[tot++];
    o->ch[0] = o->ch[1] = nill;
    o->v = val;
    o->hit = o->tms = haha;
    o->r = ran();
}
void up(Node *o) {
    o->tms = o->hit;
    for (int d = 0; d < 2; d ++)
        tomax(o->tms, o->ch[d]->tms);
}
void rotato(Node *&o, int d) {
    Node *temp = o;
    o = o->ch[d^1];
    temp->ch[d^1] = o->ch[d];
    o->ch[d] = temp;
    up(o->ch[d]);
    up(o);
}
void insert(Node *&o) {
    if (o == nill) {
        New_node(o,val,haha);
        return ;
    }
    int d = o->cmp(val);
    if (d == -1) {
        tomax(o->hit, haha);
        up(o);
    } else {
        insert(o->ch[d]);
        up(o);
        if (o->ch[d]->r > o->r) rotato(o,d^1);
    }
}
PII query(Node *o) {
    if (o == nill) return PII(0,0);
    int d = o->cmp(val);
    if (d == 0)
        return query(o->ch[0]);
    else {
        PII ret = o->hit;
        tomax(ret, o->ch[0]->tms);
        if (d == 1) 
            tomax(ret, query(o->ch[1]));
        return ret;
    }
}
PII sum(int pos) {
    PII ret = PII(0,1);
    for ( ; pos > 0; pos -= pos&-pos) 
        tomax(ret, query(head[pos]));
    return ret;
}
void add(int pos) {
    for ( ; pos <= sy.size(); pos += pos&-pos) 
        insert(head[pos]);
}
void work() {
    PII ans = PII(0,1);
    tot = 0;
    New_node(nill,0,PII(0,0));
    for (int i = 1; i <= sy.size(); i ++) head[i] = nill;
    sort(dot,dot+n);
    for (int i = 0; i < n; i ++) {
        int yy = sy[dot[i].y];
        val = dot[i].z;
        haha = sum(yy);
        haha.first ++;
        tomax(ans, haha);
        add(yy);
    }
    printf("%d %d\n", ans.first, ans.second);
}
int main() {
    int cas;
    scanf("%d", &cas);
    while (cas--) {
        scanf("%d", &n);
        sy.init();
        for (int i = 0; i < n; i ++) {
            dot[i].read();
            sy.push(dot[i].y);
        }
        sy.doit();
        work();
    }
    return 0; 
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值