CDQ 分治 学习笔记
CDQ 分治。
问题引入:P3810 【模板】三维偏序(陌上花开)
题目要我们求 $ a_j \leq a_i $ 且 $ b_j \leq b_i $ 且 $ c_j \leq c_i $ 且 $ j \ne i $ 的 \(j\) 的数量。
我们可以 sort 第一维,归并排序排第二维,树状数组求第三维。
由于题目可以取等,并且是 ≤ 偏序,所以我们要魔改一下 merge 函数。
指针跳的顺序如下:

令左指针为 \(i\),右指针为 \(j\)。如果跳的是左指针,就在树状数组中查 \(\le c_i\) 的元素个数,统计答案;如果跳的是右指针,就在树状数组中加入 \(cnt[j]\) 个 \(c_j\)。
注意:在最后需要清空树状数组。注意别出锅。
代码中的 Hash 函数是判断两组元素是否相等。
输出时输出 \(n\) 个数,而不是去重后的数的个数。
Code:
#include<bits/stdc++.h>
// #define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
struct Node{
int x,y,z;
int ans,cnt;
}a[100005],b[100005];
bool vis[100005];
int ans[100005];
bool cmp(Node x,Node y) { return (x.x>y.x||(x.x==y.x&&x.y>y.y)||(x.x==y.x&&x.y==y.y&&x.z>y.z)); }
long long Hash(Node x) { return (x.x+1)*1000000ll*1000000+(x.y+1)*1000000ll+(x.z+1); }
int n,k;
// unordered_map<int,int> cnt;
int t[200005];
const int MAXN=200001;
inline int lowbit(int x) { return x&(-x); }
void add(int p,int k)
{
for(int i=p;i<=MAXN;i+=lowbit(i)) t[i]+=k;
}
int query(int l,int r)
{
int ans=0;
for(int i=l-1;i>0;i-=lowbit(i)) ans-=t[i];
for(int i=r;i>0;i-=lowbit(i)) ans+=t[i];
return ans;
}
void merge(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
merge(l,mid);
merge(mid+1,r);
int i=mid,j=r,k=r;
while(i>=l&&j>mid)
{
if(a[j].y<=a[i].y)
{
vis[k]=1;
add(a[j].z,a[j].cnt);
b[k--]=a[j--];
}
else
{
a[i].ans+=query(1,a[i].z);
b[k--]=a[i--];
}
}
while(i>=l)
{
a[i].ans+=query(1,a[i].z);
b[k--]=a[i--];
}
while(j>mid) b[k--]=a[j--];
for(int i=l;i<=r;i++)
{
a[i]=b[i];
if(vis[i]) add(a[i].z,-a[i].cnt),vis[i]=0;
}
}
signed main()
{
n=read();
k=read();
for(int i=1;i<=n;i++)
{
a[i].x=read();
a[i].y=read();
a[i].z=read();
}
sort(a+1,a+1+n,cmp);
a[0]={-1,-1,-1};
int tot=0;
for(int i=1;i<=n;i++)
{
if(Hash(a[i-1])!=Hash(a[i])) b[++tot]=a[i];
b[tot].cnt++;
}
swap(a,b);
swap(n,tot);
merge(1,n);
for(int i=1;i<=n;i++) ans[a[i].ans+a[i].cnt-1]+=a[i].cnt;
for(int i=0;i<tot;i++)
{
write(ans[i]);
putchar('\n');
}
return 0;
}
例题:P4390 [BalkanOI 2007] Mokia 摩基亚
将操作时间 \(i\) 看作第一维,横坐标 \(x\) 看作第二维,纵坐标 \(y\) 看作第三维,题目要我们求三维偏序。
注意归并排序中 \(i\) 和 \(j\) 别写反了。
还是 ≼ 偏序而不是 < 偏序。
拆操作 2 的时候不要脑残把坐标写错了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
int MAXN;
int n;
struct Node{
signed y,z;
long long ans,cnt,op,id;
}a[1<<18],b[1<<18];
// bool cmp(Node x,Node y) { return x.x<y.x||x.x= }
int tot;
int ans[10005];
bool vis[1<<18];
// const int MA=5e5;
int t[2<<20];
int lowbit(int x) { return x&(-x); }
void add(int p,int k)
{
for(int i=p;i<=MAXN;i+=lowbit(i)) t[i]+=k;
}
int query(int l,int r)
{
int ans=0;
for(int i=l-1;i>0;i-=lowbit(i)) ans-=t[i];
for(int i=r;i>0;i-=lowbit(i)) ans+=t[i];
return ans;
}
void merge(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
merge(l,mid);
merge(mid+1,r);
int i=l,j=mid+1,k=l;
// cout<<"l="<<l<<" r="<<r<<"\n";
while(i<=mid&&j<=r)
{
if(a[i].y<=a[j].y)
{
if(!a[i].id)
{
vis[k]=1;
add(a[i].z,a[i].cnt);
// cout<<" a[i].z="<<a[i].z<<" a[i].cnt="<<a[i].cnt<<"\n";
}
b[k++]=a[i++];
}
else
{
if(a[j].id) a[j].ans+=query(1,a[j].z);
b[k++]=a[j++];
}
}
while(i<=mid) b[k++]=a[i++];
while(j<=r)
{
if(a[j].id) a[j].ans+=query(1,a[j].z);
b[k++]=a[j++];
}
for(int i=l;i<=r;i++)
{
a[i]=b[i];
if(vis[i])
{
vis[i]=0;
add(a[i].z,-a[i].cnt);
}
}
// memset(t,0,sizeof(t));
// memset(vis,0,sizeof(vis));
// cout<<"\n";
}
signed main()
{
cin>>MAXN>>MAXN;
int op;
while(cin>>op)
{
if(op==3) break;
if(op==2)
{
int x1=read(),y1=read(),x2=read(),y2=read();
++tot;
a[++n]={x1-1,y1-1,0,0,1,tot};
a[++n]={x1-1,y2,0,0,-1,tot};
a[++n]={x2,y1-1,0,0,-1,tot};
a[++n]={x2,y2,0,0,1,tot};
}
if(op==1)
{
int x=read(),y=read(),z=read();
// cout<<"("<<x<<","<<y<<") cnt="<<z<<"\n";
a[++n]={x,y,0,z,0,0};
}
}
merge(1,n);
for(int i=1;i<=n;i++)
{
// if(a[i].id) cout<<"id="<<a[i].id<<" x="<<a[i].y<<" y="<<a[i].z<<" dt="<<a[i].ans*a[i].op<<"\n";
ans[a[i].id]+=a[i].ans*a[i].op;
}
for(int i=1;i<=tot;i++)
write(ans[i]),putchar('\n');
//mt19937_64 myrand(time(0));
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/p/18972714
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号