三维偏序学习笔记
三维偏序
- 第一维,通过排序直接解决
- 第二维,通过归并排序处理
- 第三维,通过数据结构解决
void CDQ(int l,int r){
if(l==r)return;
int mid = l+r>>1,L=l,R=mid+1;
CDQ(l,mid),CDQ(mid+1,r);
for(int i=l;i<=r;++i){
if(R>r || L>=l && a[L].x<=a[R].x){
c[i] = a[L];
bit.add(a[L++].y,1);
}
else{
c[i] = a[R];
ans += bit.query(a[R++].y);
}
}
}
void CDQ(int l,int r){
if(l==r)return;
int mid = l+r>>1,L=l,R=mid+1;
CDQ(l,mid),CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp),sort(a+mid+1,a+r+1,cmp);
for(int i=l;i<=r;++i){
if(R>r || L>=l && a[L].x<=a[R].x)bit.add(a[L++].y,1);
else ans += bit.query(a[R++].y);
}
}
int main(){
sort(a+1,a+n+1);
}
动态逆序对
加入一维时间,表示删除的时间
- 二维偏序求出逆序对的数量
- 每次统计出,删除的数字构成的逆序对数量
\(i<j,a_i>a_j,t_i<t_j\)
\(i>j,a_i<a_j,t_i<t_j\)
void CDQ(int l,int r){
if(l==r)return;
int mid = l+r>>1,L=l,R=mid+1;
CDQ(l,mid),CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp),sort(a+mid+1,a+r+1,cmp);
for(int i=l;i<=r;++i){
if(R>r || L>=l && a[L].x<=a[R].x)bit.add(a[L++].y,1);
else{
ans[a[R].x] += L - i - bit.query(a[R].y)
++R;
}
}
for(int i=l;i<=mid;++i)bit.add(a[i].y,-1);
for(int i=l;i<=r;++i){
if(R>r || L>=l && a[L].x>=a[R].x)bit.add(a[L++].y,1);
else{
ans[a[R].x] ++ bit.query(a[R].y);
++R;
}
}
for(int i=l;i<=mid;++i)bit.add(a[i].y,-1);
}
CF762E
-
\(i<j\)
-
\(min(r_i,r_j)\ge |x_i-x_j|\)
-
\(|f_i-f_j|\le k\)
选择 \(r_i\) 从大到小排序
-
\(i<j\)
-
\(r_j\ge |x_i-x_j|\) -> \(r_j-x_j\le x_i\le r_j+x_j\)
-
\(f_j-k\le f_i\le f_j+k\)
第二维选择 \(f_i\) 从小到大排序,结合双指针处理
第三维选择 \(x_i\) 从小到大排序,区间查询
void CDQ(int l,int r){
if(l==r)return ;
int mid = l+r>>1,L=l,R=l-1;
CDQ(l,mid),CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp),sort(a+mid+1,a+r+1,cmp);
for(int i=mid+1;i<=R;++i){
while(R<mid && a[R+1].f<=a[i].f+k)bit.add(a[++R].x,1);
while(L>=R && a[L].f<a[i].f-k)bit.add(a[L++].x,-1);
ans += bit.query(a[i].x-a[i].r,a[i].x+a[i].r);
}
}
int main(){
sort(a+1,a+n+1,cmp1);//r 从大到小排序
}
struct P{
int r,x,y,opt;
}q[N*5];
int main(){
for(int i=1;i<=n;++i){
q[++cnt] = {r[i],x[i],f[i],0};
q[++cnt] = {r[i],r[i]-x[i]-1,f[i]-k-1,1};
q[++cnt] = {r[i],r[i]-x[i]-1,f[i]+k,2};
q[++cnt] = {r[i],r[i]+x[i],f[i]-k-1,2};
q[++cnt] = {r[i],r[i]+x[i],f[i]+k,1};
}
}
loj2056 序列
\(dp_i = max(dp[j])+1,i>j,a_i\ge a_j\)
//用归并排序实现 dp
void CDQ(int l,int r){
if(l==r)return void(tomax(dp[l],1));
int mid = l+r>>1;
CDQ(l,mid);
for(int i=mid+1;i<=r;++i)
b[i] = a[i];
sort(b+mid+1,b+r+1,cmp);
for(int i=mid+1,L=l,mx = 0;i<=r;++i){
while(L<=mid && b[i].c>=a[L].c)tomax(mx,dp[a[L++].id]);
tomax(dp[b[i].id],mx+1);
}
CDQ(mid+1,r);
sort(a+l,a+r+1,cmp);
}
\(mx_i,mi_i\)
\(dp_i = max(dp[j])+1,i>j,mi_i\ge a_j,a_i\ge mx_j\)
void CDQ(int l,int r){
if(l==r)return void(tomax(dp[l],1));
int mid = l+r>>1;
CDQ(l,mid);//左边 x=a[i],y=mx[i] 右边 x=mi[i],y=a[i]
for(int i=mid+1;i<=r;++i)c[i] = b[i];
sort(c+mid+1,c+r+1,cmp);
for(int i=mid+1,L=l;i<=r;++i){
while(L<=mid && b[L].a<=c[i].mi)bit.add(b[L].mx,dp[b[++L].id]);
tomax(dp[c[i].id],bit.query(c[i].a)+1);
}
bit.clear();
CDQ(mid+1,r);
sort(b+l,b+r+1);
}
int main(){
for(int i=1;i<=n;++i)
b[++cnt] = {mi[i],mx[i],a[i],i};
}
bzoj2001
线段树分治,把修改变成对应值改成作用域在时间区间的问题
将子树里(不包括根)的所有边视为 \(-inf\) ,尝试将根到当前结点的所有可能的边加入并查集,能加入的边在这棵子树内必选
将必选的边选完,将子树里(不包括根)的所有边视为 \(inf\) ,尝试将根到当前结点的所有可能的边加入并查集,能加入的边在这棵子树可能选中(数量级较小,和修改总次数相同)
vector<Edge> e[N<<2],g[N<<2],now[20];
void change(int p,int l,int r,int L,int R,Edge v){
if(l>=L && r<=R){
g[p].push_back(v);
return;
}
e[p].push_back(v);
int mid = l+r>>1;
if(mid>=L)change(ls,l,mid,L,R,v);
if(mid<R)change(rs,mid+1,r,L,R,v);
}
//now 记录从根到当前结点,可能在最小生成树里的边(去掉了一定和不可能的)
void dfs(int p,int l,int r,int d){
for(auto v:g[p])now[d].push_back(v);
sort(now[d].begin(),now[d].end());
int tmp = st.top;
if(l==r){
for(auto v:now[d])st.uni(v.u,v.v);
st.pop(tmp);
printf("%d\n",sum);
return;
}
for(auto v:e[p])st.uni(v.u,v.v);
for(int i=0;i<now[d].size();++i){
auto v = now[d][i];
if(st.uni(v.u,v.v))mark[i] = 1;
}
st.pop(tmp);
for(int i=0;i<now[d].size();++i){
auto v = now[d][i];
if(mark[i])st.uni(v.u,v,v),mark[i] = 0;
}
int tmp1 = st.top;
now[d+1].clear();
for(int i=0;i<now[d].size();++i){
auto v = now[d][i];
if(st.uni(v.u,v.v))now[d+1].push_back(v);
}
st.pop(tmp1);
dfs(ls,l,mid,d+1),dfs(rs,mid+1,r,d+1);
st.pop(tmp);
}

浙公网安备 33010602011771号