HDU--4742(CDQ分治,DP)
2015-08-10 15:24:45
【传送门】
题意:给出最多10^5个点,每个点有x,y,z坐标(1 <= x , y ,z <= 2^30),如果点A(x1,y1,z1)到B(x2,y2,z2)有边,当且仅当 x1 <= x2 , y1 <= y2 , z1 <= z2,问该有向图中的最长路。
思路:1维2维的情况都很好解决,三维的话在DP的基础上要用CDQ分治解决掉一维。
不妨设X为时间维,那么我们先按照X排序,在分治的时候:
(1)分治左区间
(2)处理更新,先标记出哪些点在左边(因为点的X坐标可能一样,所以不能直接取中点),然后按照Y再排序,然后遍历区间内所有点,如果之前被标记为左边点,那么用点的Z坐标更新树状数组,如果是右边点那么查询树状数组更新dp。还原之前的更新操作,重新按照X排序。
(3)分治右区间
注意点:按照X排序的话在X相同的话需要按照Z排序,按照Y排序也是一样。
#include <cstdio> #include <ctime> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB push_back typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 100010; const int mod = 1 << 30; int T,N,qmax; int B[MAXN]; int c[MAXN],tmp[MAXN]; int cnt[MAXN],qcnt; int L[MAXN]; struct Node{ int x,y,z; int id,dp,cnt; }Q[MAXN]; void Magic(int &v1,int &c1,int &v2,int &c2){ //use v2 update v1 if(v1 == v2) c1 += c2; else if(v2 > v1) v1 = v2,c1 = c2; if(c1 >= mod) c1 -= mod; } void Update(int x,int v,int num){ while(x <= N){ Magic(c[x],cnt[x],v,num); x += x & (-x); } } void Getmax(int x){ while(x){ Magic(qmax,qcnt,c[x],cnt[x]); x -= x & (-x); } } void Clear(int x){ while(x <= N){ c[x] = 0; cnt[x] = 0; x += x & (-x); } } bool cmp_x(Node a,Node b){ if(a.x != b.x) return a.x < b.x; return a.z < b.z; } bool cmp_y(Node a,Node b){ if(a.y != b.y) return a.y < b.y; return a.z < b.z; } void CDQ(int l,int r){ if(l == r) return; int mid = getmid(l,r); CDQ(l,mid); for(int i = l; i <= r; ++i){ //找出左区间Q if(i <= mid) L[Q[i].id] = 1; else L[Q[i].id] = 0; } sort(Q + l,Q + r + 1,cmp_y); //处理y维 int tmp_cnt = 0; for(int i = l; i <= r; ++i){ if(L[Q[i].id]){ tmp[++tmp_cnt] = Q[i].z; Update(Q[i].z,Q[i].dp,Q[i].cnt); } else{ qmax = 0; qcnt = 0; Getmax(Q[i].z); qmax += 1; Magic(Q[i].dp,Q[i].cnt,qmax,qcnt); } } for(int i = 1; i <= tmp_cnt; ++i) Clear(tmp[i]); sort(Q + l,Q + r + 1,cmp_x); CDQ(mid + 1,r); } int main(){ scanf("%d",&T); while(T--){ memset(c,0,sizeof(c)); memset(cnt,0,sizeof(cnt)); scanf("%d",&N); for(int i = 1; i <= N; ++i){ scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].z); Q[i].dp = 1; Q[i].cnt = 1; Q[i].id = i; B[i] = Q[i].z; } sort(B + 1,B + N + 1); int szb = unique(B + 1,B + N + 1) - B - 1; for(int i = 1; i <= N; ++i){ Q[i].z = lower_bound(B + 1,B + szb + 1,Q[i].z) - B; } sort(Q + 1,Q + N + 1,cmp_x); //x维为时间轴,分治x维 CDQ(1,N); int ans = 0,ans_cnt = 0; for(int i = 1; i <= N; ++i) Magic(ans,ans_cnt,Q[i].dp,Q[i].cnt); printf("%d %d\n",ans,ans_cnt); } return 0; }