非正解,卡时限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;
}