生活随笔
收集整理的這篇文章主要介紹了
模板:CDQ分治
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- 例題
- P3810 【模板】三維偏序(陌上花開)
- P2487 [SDOI2011]攔截導彈
所謂CDQ分治,就是和由Conprour、Doctorjellyfish、QE添一同發明的分治算法
(逃)
前言
神奇的亂搞黑科技
CDQ分治能夠通過更小的時間常數和更簡單的代碼難度完爆一些大算法,如樹套樹、splay凸包等。
應用條件:詢問離線,修改獨立
例題
P3810 【模板】三維偏序(陌上花開)
有n個元素,每個元素有三個特征值,元素a大于元素b當且僅當a的三個特征值都大于等于b
設 f(i) 表示a大于的元素數量(不含自己),對于每一個 i ,求出 f(x)=i 的 x 的數目
n≤2×105n\le 2\times10^5n≤2×105
CDQ分治的經典套路:先遞歸求左右內部貢獻,再求左右對互相的貢獻
先按照x排序,然后每次合并的時候兩邊分別按照y排序,利用雙指針維護樹狀數組累加答案即可
時間復雜度O(nlog?2n)O(n\log^2n)O(nlog2n)
細節上,注意完全相同的元素需要特殊處理,樹狀數組不要忘記清空
#include<bits/stdc++.h>
using namespace std
;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N
=2e5+100;
const int mod
=998244353;
inline ll
read() {ll
x(0),f(1);char c
=getchar();while(!isdigit(c
)) {if(c
=='-')f
=-1;c
=getchar();}while(isdigit(c
)) {x
=(x
<<1)+(x
<<3)+c
-'0';c
=getchar();}return x
*f
;
}int n
,m
,k
;
struct node{int a
,b
,c
,id
,w
;
}p
[N
],P
[N
];
bool cmpa(node u
,node v
){if(u
.a
!=v
.a
) return u
.a
<v
.a
;else if(u
.b
!=v
.b
) return u
.b
<v
.b
;return u
.c
<v
.c
;
}
bool cmpb(node u
,node v
){if(u
.b
!=v
.b
) return u
.b
<v
.b
;else return u
.c
<v
.c
;
}int f
[N
];
inline void add(int p
,int w
){for(;p
<=m
;p
+=p
&-p
) f
[p
]+=w
;return;
}
inline int ask(int p
){int res(0);for(;p
;p
-=p
&-p
) res
+=f
[p
];return res
;
}int ans
[N
],bac
[N
];
void solve(int l
,int r
){if(l
==r
) return;int mid
=(l
+r
)>>1;solve(l
,mid
);solve(mid
+1,r
);sort(p
+l
,p
+mid
+1,cmpb
);sort(p
+mid
+1,p
+r
+1,cmpb
);int pl
=l
;for(int i
=mid
+1;i
<=r
;i
++){while(pl
<=mid
&&p
[pl
].b
<=p
[i
].b
){add(p
[pl
].c
,p
[pl
].w
);++pl
;}ans
[p
[i
].id
]+=ask(p
[i
].c
);}for(int i
=l
;i
<pl
;i
++) add(p
[i
].c
,-p
[i
].w
);return;
}
int main(){
#ifndef ONLINE_JUDGE
#endifn
=read();m
=read();for(int i
=1;i
<=n
;i
++){P
[i
]=(node
){(int)read(),(int)read(),(int)read()};}sort(P
+1,P
+1+n
,cmpa
);int tot(0);for(int i
=1;i
<=n
;i
++){if(i
>1&&P
[i
-1].a
==P
[i
].a
&&P
[i
-1].b
==P
[i
].b
&&P
[i
-1].c
==P
[i
].c
) p
[tot
].w
++;else{p
[++tot
]=P
[i
];p
[tot
].w
=1;p
[tot
].id
=tot
;}}swap(tot
,n
);solve(1,n
);for(int i
=1;i
<=n
;i
++){bac
[ans
[p
[i
].id
]+p
[i
].w
-1]+=p
[i
].w
;}for(int i
=0;i
<tot
;i
++) printf("%d\n",bac
[i
]);return 0;
}
P2487 [SDOI2011]攔截導彈
某國為了防御敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度、并且能夠攔截任意速度的導彈,但是以后每一發炮彈都不能高于前一發的高度,其攔截的導彈的飛行速度也不能大于前一發。某天,雷達捕捉到敵國的導彈來襲。由于該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
在不能攔截所有的導彈的情況下,我們當然要選擇使國家損失最小、也就是攔截導彈的數量最多的方案。但是攔截導彈數量的最多的方案有可能有多個,如果有多個最優方案,那么我們會隨機選取一個作為最終的攔截導彈行動藍圖。
我方間諜已經獲取了所有敵軍導彈的高度和速度,你的任務是計算出在執行上述決策時,每枚導彈被攔截掉的概率。
利用CDQ分治優化1D-1D的DP
先遞歸處理出左半區間的dp值,然后嘗試用左半區間的dp值更新右半區間,再遞歸處理右半區間
利用樹狀數組維護前/后綴最大值保證復雜度
時間復雜度O(nlog?2n)O(n\log^2n)O(nlog2n)
注意:在遞歸處理右半區間之前,需要先按照下標重新sort一下使右半區間重新變得有序
#include<bits/stdc++.h>
using namespace std
;
#define ll __int128
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N
=2e5+100;
const int mod
=1e9;
inline ll
read() {ll
x(0),f(1);char c
=getchar();while(!isdigit(c
)){if(c
=='-')f
=-1;c
=getchar();}while(isdigit(c
)){x
=(x
<<1)+(x
<<3)+c
-'0';c
=getchar();}return x
*f
;
}int n
,m
,k
;
int q
[N
],cnt
;
struct node{int a
,b
,id
;bool operator < (const node o
){return a
>o
.a
;}
}p
[N
];
bool cmp(node u
,node v
){return u
.id
<v
.id
;
}
struct Dp{int val
;ll num
;
}dp1
[N
],dp2
[N
];
void operator += (Dp
&a
,Dp b
){if(b
.val
>a
.val
) a
=b
;else if(b
.val
==a
.val
) a
.num
+=b
.num
;return;
}Dp f
[N
];
inline void Upd(int p
,Dp w
){for(;p
<=cnt
;p
+=p
&-p
) f
[p
]+=w
;return;
}
inline Dp
Ask(int p
){Dp res
=(Dp
){0,0};for(;p
;p
-=p
&-p
) res
+=f
[p
];return res
;
}
inline void Clear(int p
){for(;p
<=cnt
;p
+=p
&-p
) f
[p
]=(Dp
){0,0};return;
}
void solve2(int l
,int r
){if(l
==r
){++dp2
[l
].val
;return;}int mid
=(l
+r
)>>1;solve2(mid
+1,r
);sort(p
+l
,p
+mid
+1);sort(p
+mid
+1,p
+r
+1);int pl
=r
;for(int i
=mid
;i
>=l
;i
--){while(pl
>mid
&&p
[pl
].a
<=p
[i
].a
){Upd(p
[pl
].b
,dp2
[p
[pl
].id
]);--pl
;}dp2
[p
[i
].id
]+=Ask(p
[i
].b
);}for(int i
=r
;i
>pl
;i
--) Clear(p
[i
].b
);sort(p
+l
,p
+mid
+1,cmp
);solve2(l
,mid
);return;
}inline void upd(int p
,Dp w
){p
=cnt
-p
+1;for(;p
<=cnt
;p
+=p
&-p
) f
[p
]+=w
;return;
}
inline Dp
ask(int p
){p
=cnt
-p
+1;Dp res
=(Dp
){0,0};for(;p
;p
-=p
&-p
) res
+=f
[p
];return res
;
}
inline void clear(int p
){p
=cnt
-p
+1;for(;p
<=cnt
;p
+=p
&-p
) f
[p
]=(Dp
){0,0};return;
}
void solve1(int l
,int r
){if(l
==r
){++dp1
[l
].val
;return;}int mid
=(l
+r
)>>1;solve1(l
,mid
);sort(p
+l
,p
+mid
+1);sort(p
+mid
+1,p
+r
+1);int pl
=l
;for(int i
=mid
+1;i
<=r
;i
++){while(pl
<=mid
&&p
[pl
].a
>=p
[i
].a
){upd(p
[pl
].b
,dp1
[p
[pl
].id
]);++pl
;}dp1
[p
[i
].id
]+=ask(p
[i
].b
);}for(int i
=l
;i
<pl
;i
++) clear(p
[i
].b
);sort(p
+mid
+1,p
+r
+1,cmp
);solve1(mid
+1,r
);return;
}int main(){
#ifndef ONLINE_JUDGE
#endifn
=read();for(int i
=1;i
<=n
;i
++){p
[i
].a
=read();p
[i
].b
=read();p
[i
].id
=i
;dp1
[i
].num
=dp2
[i
].num
=1;q
[++cnt
]=p
[i
].a
;q
[++cnt
]=p
[i
].b
;}sort(q
+1,q
+1+cnt
);cnt
=unique(q
+1,q
+1+cnt
)-q
-1;for(int i
=1;i
<=n
;i
++){p
[i
].a
=lower_bound(q
+1,q
+1+cnt
,p
[i
].a
)-q
;p
[i
].b
=lower_bound(q
+1,q
+1+cnt
,p
[i
].b
)-q
;}solve1(1,n
);sort(p
+1,p
+1+n
,cmp
);solve2(1,n
);ll sum
=0;int ans(0);for(int i
=1;i
<=n
;i
++){if(dp1
[i
].val
>ans
){ans
=dp1
[i
].val
;sum
=dp1
[i
].num
;}else if(dp1
[i
].val
==ans
){sum
+=dp1
[i
].num
;}}printf("%d\n",ans
);for(int i
=1;i
<=n
;i
++){if(dp1
[i
].val
+dp2
[i
].val
-1==ans
){printf("%.5lf ",1.0*dp1
[i
].num
*dp2
[i
].num
/sum
);}else printf("0.00000 ");}return 0;
}
總結
以上是生活随笔為你收集整理的模板:CDQ分治的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。