BZOJ 2989: 数列/4170: 极光

时间:2022-03-09 23:57:08

题解:

n倍经验题

首先比较容易想到的是对绝对值分类讨论

然后是4维偏序

1.查询和修改顺序

2.x>y

3.a[x]>a[y]

4.(x+a[x])-(y+a[y])<=k

这样是nlogn^3的,也许可以卡过吧。。。

另外注意在解决偏序问题的时候我们尽量使用cdq分治嵌套

注意cdq分治嵌套的时候

合并用归并排序这样才是nlog^3不然是nlog^4的

如果每层都要用归并的话每层要用不同数组

由于前面不对复杂度造成影响所以其实直接最后一层用归并就可以了

#include <bits/stdc++.h>
using namespace std;
#define IL inline
#define rint register int
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
char ss[<<],*A=ss,*B=ss;
char gc()
{
return A==B&&(B=(A=ss)+fread(ss,,<<,stdin),A==B)?EOF:*A++;
}
template<class T>void read(T &x)
{
rint f=,c; while (c=gc(),c<||c>) if (c=='-') f=-; x=c^;
while (c=gc(),c>&&c<) x=(x<<)+(x<<)+(c^); x*=f;
}
const int INF=1e9;
const int N=2e5+;
struct re{
int a,b,c,d,e,f,g;
}a[N],b[N];
int ans[N];
IL bool cmp(re x,re y)
{
return(x.a<y.a||(x.a==y.a&&x.b<y.b)||(x.a==y.a&&x.b==y.b&&x.c<y.c));
}
IL bool cmp2(re x,re y)
{
return(x.b<y.b||(x.b==y.b&&x.c<y.c)||(x.b==y.b&&x.c==y.c&&x.a<y.a));
}
IL bool cmp3(re x,re y)
{
return(x.c<y.c||(x.c==y.c&&x.a<y.a)||(x.c==y.c&&x.a==y.a&&x.b<y.b));
}
#define mid ((h+t)/2)
IL void cdq_fz2(int h,int t)
{
if (h==t) return;
cdq_fz2(h,mid); cdq_fz2(mid+,t);
rep(i,h,mid) a[i].f=;
rep(i,mid+,t) a[i].f=;
int h1=h,h2=mid+,h3=h;
while (h1<=mid&&h2<=t)
if (cmp3(a[h1],a[h2])) b[h3++]=a[h1++];
else b[h3++]=a[h2++];
while (h1<=mid) b[h3++]=a[h1++];
while (h2<=t) b[h3++]=a[h2++];
rep(i,h,t) a[i]=b[i];
int cnt=;
rep(i,h,t)
{
if (a[i].e&a[i].f) cnt+=a[i].d;
if (!(a[i].e|a[i].f)) a[i].g+=cnt;
}
}
IL void cdq_fz1(int h,int t)
{
if (h==t) return;
cdq_fz1(h,mid); cdq_fz1(mid+,t);
rep(i,h,mid) a[i].e=;
rep(i,mid+,t) a[i].e=;
sort(a+h,a+t+,cmp2);
cdq_fz2(h,t);
}
int main()
{
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
int n,k;
read(n); read(k); int nn=n;
rep(i,,n) read(a[i].a),read(a[i].b),read(a[i].c),a[i].d=;
sort(a+,a+n+,cmp);
int l=;
a[].a=INF;
rep(i,,n)
if (a[i].a==a[i-].a&&a[i].b==a[i-].b&&a[i].c==a[i-].c)
a[l].d++; else a[++l]=a[i];
n=l;
cdq_fz1(,n);
for(int i=;i<=n;i++) ans[a[i].g+a[i].d-]+=a[i].d;
for (int i=;i<=nn-;i++) cout<<ans[i]<<endl;
return ;
}

避免使用树套树

因为这样能大量降低编程复杂度

另外一种比较简单的方法是

当我们解不等式abs(x-y)+abs(a[x]-a[y])<=k时

先利用绝对值不等式abs(x-y+a[x]-a[y])<=abs(x-y)+abs(a[x]-a[y])<=k

得出-k<=x-y+a[x]-a[y]<=k(我并没有想出来这样为什么是等效的)

那么加上查询和修改顺序是三维偏序

正解是把它放到二维平面上

然后由于是一个菱形

将笛卡尔坐标旋转45°

然后三维偏序的cdq做法或者kd-tree就都可以了

另外本题如果强制在线除了kd-tree还有一种做法

就是二进制分组