Sol
前言
在看题目之前,先给大家安利一个好博客,博主就是用过这篇博客和巨佬YCB的帮助下学会的
想学会CDQ就戳我
题目大意
有
n
n
个元素,第个元素有三个属性
ai
a
i
,
bi
b
i
,
ci
c
i
设
f(i)
f
(
i
)
为表示满足
对于 ,求 f(i) f ( i ) = d d 的数量
题解
想必大家都是为了学习CDQ分治才来刷这道题目的(会Treap的大佬请走开)
首先我们考虑二维偏序,只需将第一维例如排序,然后在确保
a
a
有序的前提下利用各种算法算顺序对,例如Splay,归并,线段树等等
然后至于三维,四维甚至五维六维的,其实就是不断的往上套数据结构(或者继续CDQ分支)罢了,太高序的其实只是套板子的过程了(
貌似低维也是套板子的过程)
本题我采用的是
归并套树状数组(貌似楼上没有这种算法)
先将一维排序,然后再用归并排序,值得注意的是
在左区间A,和右区间B中,区间A的a一定小于B,但是A内的a再归并后是否还有序就不一定了!!!
再归并左区间过程中只修改(因为只算对右区间的贡献!!!其内部的贡献会在下层递归中找到),在右区间过程中才查询!!!
然后我的代码用的是
左闭右闭的区间,这样更加方便
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define ll long long
const int _=200100;
inline int read()
{
char ch='!';int z=1,num=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')z=-1,ch=getchar();
while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
return z*num;
}
int N,K,top,bel[_];
ll ans[_],g[_],c[_],f[_];
struct hand
{
int a,b,c,sz,id;
bool operator < (const hand &A)const
{
if(b!=A.b)return b<A.b;
if(c!=A.c)return c<A.c;
return id<A.id;
}
}t[_],tmp[_],q[_];
bool cmp(hand A,hand B)
{
if(A.a!=B.a)return A.a<B.a;
if(A.b!=B.b)return A.b<B.b;
if(A.c!=B.c)return A.c<B.c;
return A.id<B.id;
}
void Update(int x,int d)
{
while(x<=K)
{
c[x]+=d;
x+=lowbit(x);
}
}
void Clear(int x,int d)
{
while(x<=K)
{
c[x]=d;
x+=lowbit(x);
}
}
int Query(int x)
{
int sum=0;
while(x)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
void CDQ(int l,int r)
{
if(l==r)return;
CDQ(l,mid);CDQ(mid+1,r);
int p=l,q=mid+1,o=l-1;
while(p<=mid&&q<=r)
{
if(t[p]<t[q])
Update(t[p].c,t[p].sz),tmp[++o]=t[p++];
else
g[t[q].id]+=Query(t[q].c),tmp[++o]=t[q++];
}
while(p<=mid)tmp[++o]=t[p++];
while(q<=r)
g[t[q].id]+=Query(t[q].c),tmp[++o]=t[q++];
for(int i=l;i<=r;++i)
Clear(tmp[i].c,0),t[i]=tmp[i];
}
bool pd(int i,int now)
{
if(!now)return 0;
else return (q[i].a==t[now].a&&q[i].b==t[now].b&&q[i].c==t[now].c);
}
int main()
{
N=read();K=read();
for(int i=1;i<=N;++i)
q[i].a=read(),q[i].b=read(),q[i].c=read(),q[i].sz=1,q[i].id=i;
sort(q+1,q+1+N,cmp);
top=0;
for(int i=1;i<=N;++i)
{
if(!pd(i,top))
t[++top]=q[i],t[top].id=top;
else t[top].sz++;
bel[i]=top;
}
CDQ(1,top);
for(int i=1;i<=top;++i)g[t[i].id]+=t[i].sz-1;
for(int i=1;i<=N;++i)f[i]=g[bel[i]];
for(int i=1;i<=N;++i)ans[f[i]]++;
for(int i=0;i<N;++i)printf("%lld\n",ans[i]);
return 0;
}
其实这里还有一个骚操作
一维相当于修改时间,二维就是修改和查询优先级
模板树状数组1
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define mid ((l+r)>>1)
const int _=500100;
inline int read()
{
char ch='!';int z=1,num=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')z=-1,ch=getchar();
while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
return z*num;
}
int N,M,top,cnt;
ll ans[_];
struct hand
{
int p,v,op;
bool operator <(const hand &A)const
{
if(p!=A.p)return p<A.p;
return op<A.op;
}
}t[_<<2],tmp[_<<2];
void CDQ(int l,int r)
{
if(l==r)return;
CDQ(l,mid);CDQ(mid+1,r);
int p=l,q=mid+1,o=l-1;
ll sum=0;
while(p<=mid&&q<=r)
{
if(t[p]<t[q])
{
if(t[p].op==1)sum+=t[p].v;
tmp[++o]=t[p++];
}
else
{
if(t[q].op==2)ans[t[q].v]-=sum;
else if(t[q].op==3)ans[t[q].v]+=sum;
tmp[++o]=t[q++];
}
}
while(p<=mid)tmp[++o]=t[p++];
while(q<=r)
{
if(t[q].op==2)ans[t[q].v]-=sum;
else if(t[q].op==3)ans[t[q].v]+=sum;
tmp[++o]=t[q++];
}
for(int i=l;i<=r;++i)
t[i]=tmp[i];
}
int main()
{
N=read();M=read();
for(int i=1;i<=N;++i)
t[++top]=(hand){i,read(),1};
for(int i=1;i<=M;++i)
{
int op=read(),x=read(),y=read();
if(op&1)
t[++top]=(hand){x,y,1};
else
{
cnt++;
t[++top]=(hand){x-1,cnt,2};
t[++top]=(hand){y,cnt,3};
}
}
CDQ(1,top);
for(int i=1;i<=cnt;++i)
printf("%lld\n",ans[i]);
return 0;
}