[BZOJ3262] 陌上花开

传送门

http://www.lydsy.com/JudgeOnline/problem.php?id=3262

题目大意

(xi,yi,zi)xi<=xj,yi<=yj,zi<=zji<j,

题解

三维偏序
cdq分治
我们对三维按x为第一关键字,y为第二关键字.z为第三关键字排序
这样我们得到的每个点前面的点x那维一定小于等于自己
然后我们分治y那维,这个过程类似归并排序的过程
对于我们分治时相邻的区间,我们把两个区间内的点只看y那维合并成一个区间
对于左边的区间会对右边区间产生贡献(因为x值<=右边的任意一个)
这个贡献我们把他们的z值插进树状数组中
当右区间指针的点因为比左区间指针的点而踢出时,查询树状数组中小于等于它z值得点个数
这样就可以优化掉一位
不清楚的画图看看,注意相同的先和成一个点,插进树状数组时是sum值不是1,每次树状数组要清0

type
    data=record
    x,y,z,num,ans:longint; end;
var
    t,temp:array[0..100005]of data;
    bite,ans:array[0..200005]of longint;
    i,j,k:longint;
    n,m,s:longint;
procedure sort(l,r:longint);
var i,j,a,b,c:longint; d:data;
begin
 i:=l; j:=r; a:=t[(l+r) div 2].x; b:=t[(l+r)div 2].y; c:=t[(l+r)div 2].z;
 repeat
  while (t[i].x<a)or((t[i].x=a)and(t[i].y<b))or((t[i].x=a)and(t[i].y=b)and(t[i].z<c)) do inc(i);
  while (a<t[j].x)or((t[j].x=a)and(t[j].y>b))or((t[j].x=a)and(t[j].y=b)and(t[j].z>c)) do dec(j);
  if not(i>j) then
   begin
    d:=t[i]; t[i]:=t[j]; t[j]:=d;
    inc(i); dec(j);
   end;
 until i>j;
 if l<j then sort(l,j);
 if i<r then sort(i,r);
end;

procedure update(a,b:longint);
var tt:longint;
begin
    tt:=a;
    while tt<=s do
        begin
            inc(bite[tt],b);
            inc(tt,tt and (-tt));
        end;
end;

function query(a:longint):longint;
var tt,sum:longint;
begin
    tt:=a; sum:=0;
    while tt>0 do
        begin
            inc(sum,bite[tt]);
            dec(tt,tt and (-tt));
        end;
    exit(sum);
end;

procedure cdq(l,r:longint);
var i,j,k,mid,len:longint;
begin
    if l=r then exit;
    mid:=(l+r)>>1;
    cdq(l,mid); cdq(mid+1,r);
    i:=l; j:=mid+1; len:=l;
    while (i<=mid)and(j<=r) do
        begin
            if (t[i].y<=t[j].y)
            then begin update(t[i].z,t[i].num); temp[len]:=t[i]; inc(i); inc(len); continue; end
            else begin inc(t[j].ans,query(t[j].z)); temp[len]:=t[j]; inc(j); inc(len); end;
        end;
    for k:=j to r do
        begin inc(t[k].ans,query(t[k].z)); temp[len]:=t[k]; inc(len); end;
    for k:=l to mid do
        if k>=i
        then begin temp[len]:=t[k]; inc(len); end
        else update(t[k].z,-t[k].num);
    for i:=l to r do
        t[i]:=temp[i];
end;

begin
    readln(n,s);
    for i:=1 to n do
        readln(t[i].x,t[i].y,t[i].z);
    sort(1,n); {t[i].x}
    m:=0; t[0].x:=0; t[0].y:=0; t[0].z:=0;
    for i:=1 to n do
        begin t[i].ans:=0; t[i].num:=0; end;
    for i:=1 to n do
        if (t[i].x=t[i-1].x)and(t[i].y=t[i-1].y)and(t[i].z=t[i-1].z)
        then inc(t[m].num)
        else begin inc(m); t[m]:=t[i]; t[m].num:=1; end;
    cdq(1,m);
    fillchar(ans,sizeof(ans),0);
    for i:=1 to n do
        inc(ans[t[i].ans+t[i].num-1],t[i].num);
    for i:=0 to n-1 do
        writeln(ans[i]);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值