[BZOJ 1176]Balkan2007 Mokia cdq分治一血!

久仰cdq(分治)大名,今日才来造访,礼数不周也。。。。。(发个牢骚~)

所谓cdq分治,就是分治所有操作,计算[l,mid]中的修改对[mid+1,r]中的询问的影响。

无法理解的同学可以借助归并排序的思想思考。

抄袭别人讲以上的话吧

1、T(n)=2T(n/2)+O(kn)的解是T(n)=O(kn log n) 

2、T(n)=2T(n/2)+O(kn log n)的解是T(n)=O(kn log^2 n) 

3、T(n)=2T(n/2)+O(k)的解是T(n)=O(kn)


所以cdq分治的复杂度也可以简单的算出来了。

cdq能处理的题目必须满足:1、允许离线  2、前面的修改对后面的影响很容易算出来


比如说这一题

在[l,mid]部分的修改按x排序从小到大加进一个树状数组里,不断更新对[mid+1,r]的影响。

复杂度就是第二种计算方式,可就是O(q log^2 w) 

猥琐的code如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
#define lowbit(x) ((x)&(-(x)))
const int Maxn=200000;
int x1[Maxn],x2[Maxn],y1[Maxn],y2[Maxn],tr[Maxn];
int a[Maxn],b[Maxn],c[Maxn],k[Maxn],ans[Maxn];
int n,m,s,w,i;
struct arr{
  int x,l,r,k,c;
  bool operator <(const arr &a)const
    { return x<a.x; }
} qk[Maxn];
struct ad{
  int x,y,w;
  bool operator <(const ad &a)const
    { return x<a.x; }
} g[Maxn];
 
int query(int x){
  int ret=0;
  for (int i=x;i>0;i-=lowbit(i))
    ret+=tr[i];
  return ret;
}
 
void ins(int x,int s){
  for (int i=x;i<=m;i+=lowbit(i))
    tr[i]+=s;
}
 
void merge(int l,int r){
  if (l==r) return;
  int mid=(l+r)>>1, t=0, tt=0, i, j;
  merge(l,mid);
   
  for (i=mid+1;i<=r;i++)
  if (k[i]==2){
    qk[++t]=(arr){x1[i]-1, y1[i], y2[i], i, 0};
    qk[++t]=(arr){x2[i],   y1[i], y2[i], i, 1};
  }
  sort(qk+1,qk+t+1);
   
  for (i=l;i<=mid;i++)
  if (k[i]==1) g[++tt]=(ad){x1[i],y1[i],x2[i]};
  sort(g+1,g+tt+1);
   
  for (i=1,j=1;i<=tt;i++){
    while (j<=t && qk[j].x<g[i].x){
      if (qk[j].c==0)
        ans[qk[j].k]-=query(qk[j].r)-query(qk[j].l-1);
      else ans[qk[j].k]+=query(qk[j].r)-query(qk[j].l-1);
      j++;
    }
    ins(g[i].y,g[i].w);
  }
  while (j<=t){
    if (qk[j].c==0)
      ans[qk[j].k]-=query(qk[j].r)-query(qk[j].l-1);
    else ans[qk[j].k]+=query(qk[j].r)-query(qk[j].l-1);
    j++;
  }
   
  for (i=1;i<=tt;i++)
    ins(g[i].y,-g[i].w);
   
  merge(mid+1,r);
}
 
int main(){
  //freopen("1176.in","r",stdin);
  //freopen("1176.out","w",stdout);
  scanf("%d%d",&s,&w);
  n=1;
  while (true){
    scanf("%d",&k[n]);
    if (k[n]==0 || k[n]==3) break;
    if (k[n]==1){
      scanf("%d%d%d",&x1[n],&y1[n],&x2[n]);
      c[++m]=y1[n];
    }else
    if (k[n]==2){
      scanf("%d%d%d%d",&x1[n],&y1[n],&x2[n],&y2[n]);
      ans[n]+=s*(x2[n]-x1[n]+1)*(y2[n]-y1[n]+1);
      c[++m]=y1[n]; c[++m]=y2[n];
    }
    n++;
  }
  sort(c+1,c+m+1);
  m=unique(c+1,c+m+1)-c-1;
  for (i=1;i<n;i++){
    y1[i]=lower_bound(c+1,c+m+1,y1[i])-c;
    if (k[i]==2)
      y2[i]=lower_bound(c+1,c+m+1,y2[i])-c;
  }
  merge(1,--n);
  for (i=1;i<=n;i++)
    if (k[i]==2) printf("%d\n",ans[i]);
  return 0;
}


cdq分治的精华远不止如此,今后慢慢研习微笑


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值