HDU 4742 (CDQ分治 树状数组)

题目链接:点击这里

题意:三维LIS。给出空间的n个点,求出最长的序列和方案数,要求序列的后一个点三个坐标都不小于前一个点。

先排序掉一维,然后剩下两维分治, 用一个带长度和方案数的结构体树状数组维护。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <map>
using namespace std;
#define maxn 100005
#define mod (1LL<<30)

struct node {
    int id;
    int x, y, z;
    bool operator == (const node &a) const {
        return x == a.x && y == a.y && z == a.z;
    }
}qu[maxn], tmp[maxn];
struct Node {
    int len, num;
} ans[maxn], c[maxn];
int n;

bool cmp1 (const node &a, const node &b) {
    if (a.z != b.z)
        return a.z < b.z;
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

bool cmp2 (const node &a, const node &b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

int lowbit (int x) {
    return x&(-x);
}

void judge (Node &a, Node b) {
    if (a.len < b.len) a = b;
    else if (a.len == b.len) a.num += b.num, a.num %= mod;
}

void update (int x, Node tmp) {
    for (int i = x; i < maxn; i += lowbit (i)) {
        judge (c[i], tmp);
    }
}

Node query (int x) {
    Node ans = (Node) {0, 0};
    for (int i = x; i > 0; i -= lowbit (i)) {
        judge (ans, c[i]);
    }
    return ans;
}

void clear (int x) {
    for (int i = x; i < maxn; i += lowbit (i)) 
        c[i] = (Node) {0, 0};
}

void solve (int l, int r) { 
    if (l == r)
        return ;
    int mid = (l+r)>>1;
    solve (l, mid); 
    for (int i = l; i <= r; i++) tmp[i] = qu[i];
    sort (tmp+l, tmp+mid+1, cmp2);
    sort (tmp+mid+1, tmp+r+1, cmp2);
    int L = l;
    for (int i = mid+1; i <= r; i++) { 
        while (L <= mid && tmp[L].x <= tmp[i].x) {
            update (tmp[L].y, ans[tmp[L].id]);
            L++;
        }
        Node cur = query (tmp[i].y);
        cur.len++;
        judge (ans[tmp[i].id], cur);
    }
    for (int i = l; i <= mid; i++) {
        clear (tmp[i].y);
    }
    solve (mid+1, r);
}

int num[maxn], tot;
int gg[maxn];
void lisanhua () {
    sort (num, num+tot);
    int cnt = 0;
    for (int i = 0; i < tot; i++) {
        if (!i || num[i] != num[i-1]) gg[cnt++] = num[i];
    }
    for (int i = 1; i <= n; i++) {
        qu[i].y = lower_bound (gg, gg+cnt, qu[i].y)-gg+1;
    }
}

int scan () {
    char ch=' ';
    while(ch<'0'||ch>'9')ch=getchar();
    int x=0;
    while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar();
    return x;
}

int main () {
    int t;
    scanf ("%d", &t);
    while (t--) {
        memset (c, 0, sizeof c);
        tot = 0;
        scanf ("%d", &n);
        for (int i = 1; i <= n; i++) ans[i] = (Node) {1, 1};
        for (int i = 1; i <= n; i++) {
            qu[i].x = scan (), qu[i].y = scan (), qu[i].z = scan ();
            num[tot++] = qu[i].y;
            qu[i].id = i;
        }
        lisanhua ();
        sort (qu+1, qu+1+n, cmp1);
        solve (1, n);
        int Max = 0;
        Node res = (Node) {0, 0};
        for (int i = 1; i <= n; i++) judge (res, ans[i]);
        printf ("%d %d\n", res.len, res.num);
    }
    return 0;
}
/*
10
5
1 1 1
0 2 1
0 2 3
1 2 0
1 0 3
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值