bzoj3262 陌上花开

传送门

CDQ分治。
今天学姐讲课讲到了这个东西,所以就拿这道例题练练手(于是就给自己挖了一个大坑)
由于学姐并没有给代码,于是到网上找了题解当板子学(from Po姐
zyz大神独立码出CDQ分治,吓得蒟蒻赶紧膜拜(其实是拿来代码看。。。)
于是就出事了。
zyz大神的方法是先无脑向左右CDQ下去,然后按第三维sort、计算右半边的答案,最后按照第二维排序;
Po姐的方法是先按第一维sort,再向左CDQ,计算答案,再向右CDQ,最后按照第二维排序。
由于CDQ的时间不同,排序方法也不同,导致蒟蒻调了一晚上。。。
感觉还是zyz大神的方式比较适合食用。
顺便说一下一个非常显然但是总是让蒟蒻搞错的地方:STL的sort有两个指针。。第一个指针指向开始节点。。第二个指针指向结束节点。。然而。。这个结束节点是不会被用来排序的!是不会被用来排序的!是不会被用来排序的!
这导致我对zyz大神的代码误解了n个小时。。。mdzz
扔一下zyz大神的blog,可以用来学习算法,观摩代码就算了,,,码风过于独特,儿童慎入!

#include<cstdio>
#include<algorithm>
using namespace std;
#define lowbit(x) x&(-x)
const int N=1e5+10;
struct node
{
    int x,y,z,cnt,ans;
    inline bool operator <(const node other)const
    {
        if(x==other.x)
          if(y==other.y) return z<other.z;
          else return y<other.y;
        else return x<other.x;
    }
}a[N],t[N];
int ans[N],bit[N<<1],Time[N<<1];
int n,k,tot,Times;
inline bool cmp(const node a,const node b)
{
    if(a.y==b.y) return a.z<b.z;
    return a.y<b.y;
}
inline void add(int i,int num)
{
    for(;i<=k;i+=lowbit(i))
      if(Time[i]==Times) bit[i]+=num;
      else bit[i]=num,Time[i]=Times;
}
inline int ask(int i)
{
    int ans=0;
    for(;i;i-=lowbit(i))
      if(Time[i]==Times) ans+=bit[i];
    return ans;
}
void CDQ(int l,int r)
{
    if(l==r)
    {
        a[l].ans+=a[l].cnt-1;
        return;
    }
    int mid=(l+r)>>1,tmp1=l,tmp2=mid+1,tmp=l;
    for(int i=l;i<=r;i++)
      if(a[i].x<=mid&&tmp1<=mid) t[tmp1++]=a[i];
      else t[tmp2++]=a[i];
    for(int i=l;i<=r;i++)
      a[i]=t[i];
    CDQ(l,mid);
    Times++;
    for(int i=mid+1;i<=r;i++)
    {
        while(tmp<=mid&&a[tmp].y<=a[i].y) add(a[tmp].z,a[tmp].cnt),tmp++;
        a[i].ans+=ask(a[i].z);
    }
    CDQ(mid+1,r);
    tmp1=l,tmp2=mid+1;
    for(int i=l;i<=r;i++)
      if(tmp1<=mid&&(tmp2>r||a[tmp1].y<=a[tmp2].y)) t[i]=a[tmp1++];
      else t[i]=a[tmp2++];
    for(int i=l;i<=r;i++)
      a[i]=t[i];
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
      scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].cnt=1;
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
      if(a[i].x!=a[i-1].x||a[i].y!=a[i-1].y||a[i].z!=a[i-1].z) a[++tot]=a[i];
      else a[tot].cnt++;
    for(int i=1;i<=tot;i++)
      a[i].x=i;
    sort(a+1,a+tot+1,cmp);
    CDQ(1,tot);
    for(int i=1;i<=tot;i++)
      ans[a[i].ans]+=a[i].cnt;
    for(int i=0;i<n;i++)
      printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值