hdu 4742 Pinball Game 3D(三维LIS&cdq分治&BIT维护最值)

Pinball Game 3D

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 688    Accepted Submission(s): 276


Problem Description
RD is a smart boy and excel in pinball game. However, playing common 2D pinball game for a great number of times results in accumulating tedium.

Recently, RD has found a new type of pinball game, a 3D pinball game. The 3D pinball game space can be regarded as a three dimensional coordinate system containing N balls. A ball can be considered as a point. At the beginning, RD made a shot and hit a ball. The ball hit by RD will move and may hit another ball and the “another ball” may move and hit another another ball, etc. But once a ball hit another ball, it will disappear.

RD is skilled in this kind of game, so he is able to control every ball's moving direction. But there is a limitation: if ball A's coordinate is (x1,y1,z1) and ball B's coordinate is (x2,y2,z2), then A can hit B only if x1 <= x2 and y1 <= y2 and z1 <= z2.

Now, you should help RD to calculate the maximum number of balls that can be hit and the number of different shooting schemes that can achieve that number. Two schemes are different if the sets of hit balls are not the same. The order doesn't matter.
 

Input
The first line contains one integer T indicating the number of cases.
In each case, the first line contains one integer N indicating the number of balls.
The next N lines each contains three non-negative integer (x, y, z), indicating the coordinate of a ball.
The data satisfies T <= 3, N <= 10 5, 0 <= x, y, z <= 2 30, no two balls have the same coordinate in one case.
 

Output
Print two integers for each case in a line, indicating the maximum number of balls that can be hit and the number of different shooting schemes. As the number of schemes can be quite large, you should output this number mod 2 30.
 

Sample Input
   
   
   
   
2 3 2 0 0 0 1 0 0 1 1 5 3 0 0 0 1 0 0 0 1 0 2 2 3 3 3
 

Sample Output
   
   
   
   
2 1 3 2
Hint
In the first case, RD can shoot the second ball at first and hit the third ball indirectly. In the second case, RD can shoot the second or the third ball initially and hit the fourth ball as well as the fifth ball. Two schemes are both the best.
 

Source
2013 ACM/ICPC Asia Regional Hangzhou Online
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:   5061  5059  5058  5053  5052 
  题意:
就是给你n(1e5)个三元组、然后要你求这n个三元组的LIS。和这样LIS的方案数。一个三元祖a比另一个元祖b大的条件是ax>=bx,ay>=by,az>=bz。
思路:
如果是一维的就很好做。排个序就完了。两维的话。对x排序。然后dp[i]表示以i结尾最长序列长度.dp[i]=max(dp[i],dp[j]+1)。j<i&&y[j]<y[i]。三维的话就要用到分治的思想了。我们先按x排序x一样按y排。y一样再按x排。这样就能保证dp[i]一定是由1~i-1转移来的。然后我们就可以分治了。比如我们要算[l,r]的dp值。mid=(L+r)/2假设我们已经算出了[l,mid]的dp值。我们是不是可以用[l,mid]的dp值去更新[mid+1,r]的dp值。因为dp[i]一定是由1~i-1转移来的所以我们可以建一个树状数组维护z值为zz的最大dp(由于这题每次都是查询[1,x]整个区间所以可以用树状数组来维护最值)。当然z值是离散化后的值。然后我们两边的y排序.然后对于[mid+1,r]的每一个dp我们按y值从小到大更新。先把[l,mid]中y值比自己小的插到树状数组中。然后查询数组数组中z小于自己z的最大dp就行了。这样就可以保证树状数组中的元素x,y都是合理的然后查询的z也是合理的。然后递归处理就行了。先solve(l,mid)然后更新[mid+1,r]再solve(mid+1,r)。当然递归的时候也可以顺便进行一些排序操作(归并排序)以提高效率。
详细见代码:
#include<algorithm>
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100010;
const int mod=1<<30;
typedef long long ll;
struct node
{
    int x,y,z;
    inline bool operator <(const node& tt) const
    {
        if(x!=tt.x)
            return x<tt.x;
        if(y!=tt.y)
            return y<tt.y;
        return z<tt.z;
    }
} po[maxn];
int dp[maxn],sy[maxn],sz[maxn],mv[maxn],ans;
int tmp[maxn<<1],ty[maxn],ptr;
ll num[maxn],nv[maxn],ansn;
bool cmp(int a,int b)//注意y必须这么排.不然可能出现y相等的时候i+1排到i前面的情况.
{
    if(po[a].y!=po[b].y)
        return po[a].y<po[b].y;
    return a<b;
}
void update(int x,int v,ll d,int mn)
{
    for(int i=x;i<=mn;i+=i&-i)
    {
        if(v>mv[i])
            mv[i]=v,nv[i]=d;
        else if(v==mv[i])
            nv[i]+=d;
    }
}
int qu(int x,ll &nn)
{
    int ret=0;
    for(int i=x;i>0;i-=i&-i)
    {
        if(ret<mv[i])
            ret=mv[i],nn=nv[i];
        else if(mv[i]==ret)
            nn+=nv[i];
    }
    return ret;
}
void solve(int l,int r)
{
    if(l==r)
    {
        if(dp[l]>ans)
            ans=dp[l],ansn=num[l];
        else if(dp[l]==ans)
            ansn+=num[l];
        sz[l]=po[l].z;
        return;
    }
    int mid=(l+r)>>1,le=l,ri=mid+1,len=r-l+1,ml=mid-l+1,lim,h,i;
    memmove(tmp+ptr,sy+l,len*sizeof(int));
    for(i=0;i<len;i++)
        if(tmp[ptr+i]<=mid)//直接把排好的序划分下去就行了。
            sy[le++]=tmp[ptr+i];
        else
            sy[ri++]=tmp[ptr+i];
    ptr+=len;
    solve(l,mid);
    lim=ptr,ptr-=len;
    for(i=1;i<=ml;i++)
        mv[i]=0;
    for(i=ptr;i<lim;i++)
    {
        if(tmp[i]<=mid)
        {
            h=lower_bound(sz+l,sz+l+ml,po[tmp[i]].z)-(sz+l)+1;
            update(h,dp[tmp[i]],num[tmp[i]],ml);
            continue;
        }
        h=lower_bound(sz+l,sz+l+ml,po[tmp[i]].z)-(sz+l);
        if(h>=ml||sz[l+h]>po[tmp[i]].z)
            h--;
        if(h>=0)
        {
            ll cc=0;
            int tt=qu(h+1,cc)+1;
            if(tt>dp[tmp[i]])
                dp[tmp[i]]=tt,num[tmp[i]]=cc;
            else if(tt==dp[tmp[i]]&&tt!=1)
                num[tmp[i]]+=cc;
        }
    }
    solve(mid+1,r);
    merge(sz+l,sz+l+ml,sz+l+ml,sz+l+len,ty);
    memmove(sz+l,ty,len*sizeof(int));
}
int main()
{
    int t,n,i;

    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&po[i].x,&po[i].y,&po[i].z);
        ansn=ptr=ans=0;
        sort(po+1,po+n+1);
        for(i=1;i<=n;i++)
            sy[i]=i,dp[i]=1,num[i]=1;
        sort(sy+1,sy+n+1,cmp);
        solve(1,n);
        printf("%d %I64d\n",ans,ansn%mod);
    }
    return 0;
}
/*
送一组数据。网上好多代码都过不了这组。就是因为排序y的时候i+1排到i前面去了。导致少更新很多
杭电数据比较水就水过去了。
1
17
2 0 0
2 1 1
1 2 1
1 0 0
0 0 1
0 2 0
0 2 2
0 1 1
2 0 1
1 2 2
0 1 0
2 0 2
2 2 2
0 1 2
1 0 2
1 1 1
0 0 2
*/


你可能感兴趣的:(c,算法,ACM)