CDQ 分治
CDQ 分治可以处理一些点对 \((i, j)\),将其分为左区间和右区间,并统计左右区间之间的贡献
P3810 【模板】三维偏序 / 陌上花开
这题要注意去重(好麻烦哦)
先将 \(a\) 排序,然后 CDQ 分治
分治时要保证区间 \((l, r)\) 的 \(b\) 是有序的,虽然 \(a\) 被打乱了,但是左区间任意的 \(a\) 还是小于右区间任意的 \(a\) 的
统计答案的时候仿照归并排序,然后维护树状数组
复杂度是 \(O(n \ logn \ logk)\),离散化以后是 \(O(n \ log^2n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 1e5+5, E = 2e5+5;
int n, k, cnt, tree[E], ans[N], bucket[N], t[N];
struct Element{
int a, b, c, id, num;
}tmp[N], a[N];
inline ll read(){
ll s=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return s*f;
}
bool cmp(Element x, Element y){
if(x.a != y.a)return x.a < y.a;
if(x.b != y.b)return x.b < y.b;
return x.c < y.c;
}
void add(int x, int c){
for(;x<=k;x+=x&-x)tree[x] += c;
}
int query(int x){
int ans = 0;
for(;x;x-=x&-x)ans += tree[x];
return ans;
}
void CDQ(int l, int r){
if(l == r)return;
int mid = l + r >> 1;
CDQ(l, mid);
CDQ(mid+1, r);
int pos1 = l, pos2 = mid + 1, pos = l;
while(pos1 <= mid || pos2 <= r){
if(pos2 > r || pos1 <= mid && a[pos1].b <= a[pos2].b){
add(a[pos1].c, a[pos1].num);
tmp[pos++] = a[pos1++];
}else{
ans[a[pos2].id] += query(a[pos2].c);
tmp[pos++] = a[pos2++];
}
}
for(int i=l;i<=mid;i++)add(a[i].c, -a[i].num);
for(int i=l;i<=r;i++)a[i] = tmp[i];
}
int main(){
n = read(), k = read();
for(int i=1;i<=n;i++)tmp[i] = Element{read(), read(), read()};
sort(tmp+1, tmp+n+1, cmp);
for(int i=1;i<=n;i++){
if(tmp[i].a != tmp[i-1].a || tmp[i].b != tmp[i-1].b || tmp[i].c != tmp[i-1].c)a[++cnt] = tmp[i];
a[cnt].num++;
a[cnt].id = cnt;
t[cnt] ++;//CDQ之后顺序会打乱,还要再记录一次点对数量
}
CDQ(1, cnt);
for(int i=1;i<=cnt;i++)ans[i] += t[i]-1, bucket[ans[i]] += t[i];
for(int i=0;i<n;i++)cout << bucket[i] << endl;
return 0;
}
\(\,\)
P4390 [BalkanOI 2007] Mokia 摩基亚
这篇题解好好呀,好喜欢这样的题解,既清楚又有趣
很多细节放代码里了xixi
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" = "<<x<<endl;
const int N = 2e5 + 5, E = 2e6+5;
int w, cnt, qcnt, opt, ans[10005], tree[E];
struct Element{
int x, y, opt, val, f, id;
}a[N], tmp[N];
inline ll read(){
ll s=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return s*f;
}
bool cmp(Element a, Element b){
if(a.id != b.id)return a.id < b.id; // 按时间戳排序,这样可以保证统计的答案都是合法的
return a.opt > b.opt; // 将询问操作排在前面
}
void add(int x, int k){
for(;x<=w;x+=x&-x)tree[x] += k;
}
int query(int x){
int ans = 0;
for(;x;x-=x&-x)ans += tree[x];
return ans;
}
void CDQ(int l, int r){
if(l == r)return;
int mid = l + r >> 1;
CDQ(l, mid);
CDQ(mid+1, r);
int pos = l, pos1 = l, pos2 = mid+1;
while(pos <= r){
if(pos2 > r || pos1 <= mid && a[pos1].x <= a[pos2].x){
if(a[pos1].opt == 1)add(a[pos1].y, a[pos1].val);
tmp[pos++] = a[pos1++];
}else{
if(a[pos2].opt == 2)ans[a[pos2].id] += a[pos2].f * query(a[pos2].y);
tmp[pos++] = a[pos2++];
}
}
for(int i=l;i<=mid;i++)if(a[i].opt == 1)add(a[i].y, -a[i].val);
for(int i=l;i<=r;i++)a[i] = tmp[i];
}
int main(){
opt = read(), w = read() + 1;
// w x y 整体 + 1,防止树状数组取 0
while(opt = read()){
if(opt == 3)break;
if(opt == 1)a[++cnt] = {read() + 1, read() + 1, 1, read(), 0, qcnt};
if(opt == 2){
int xa = read() + 1, ya = read() + 1, xb = read() + 1, yb = read() + 1;
a[++cnt] = {xa-1, ya-1, 2, 0, 1, ++qcnt};
a[++cnt] = {xb, ya-1, 2, 0, -1, qcnt};
a[++cnt] = {xa-1, yb, 2, 0, -1, qcnt};
a[++cnt] = {xb, yb, 2, 0, 1, qcnt};
}
}
sort(a+1, a+cnt+1, cmp);
CDQ(1, cnt);
for(int i=1;i<=qcnt;i++)cout << ans[i] << endl;
return 0;
}

浙公网安备 33010602011771号