sklearn-機器學習庫
前言
(什麼時候我也開始寫前言了
昨天的講課內容包括CDQ分治……
有一說一,有點像紙人套幻覺套祕偶套分身套歷史投影的某套娃大師·占卜家序列。
CDQ分治每一次劃分出來兩個子問題,前一個子問題用來解決後一個子問題。主要用來解決偏序問題。
使用CDQ分治時要注意題目要求:修改操作對詢問的貢獻獨立,互不影響;允許使用離線演算法。
三維偏序
題目:
P3810 【模板】三維偏序(陌上花開
題目描述:
有 $ n $ 個元素,第 $ i $ 個元素有 $a_i,b_i,c_i $三個屬性,設 $ f(i) $ 表示滿足 $a_j ≤ a_i $ 且 $ b_j ≤ b_i $且 $ c_j ≤ c_i $且 \(j≠i\)
對於 \(d∈[0,n)\) 求 $ f(i)=d $ 的數量。
抽象理解就是:對於一個元素\(i\) 來說,\(f(i)\) 指 ,三種屬性都比\(i\)菜的元素的數量。
那麼二維偏序指 :對於一個元素\(i\) 來說,\(f(i)\) 指 ,兩種屬性都比\(i\)菜的元素的數量。
那麼一維偏序指: 對於一個元素\(i\) 來說,\(f(i)\) 指 ,一種屬性比\(i\)菜的元素的數量。
一維偏序
細品一下剛剛順下來的一維偏序的定義。
每個元素有一個權值,我們要找有幾個權值比某元素小的
於是這就成了比大小的排序問題。
所以一維偏序就是那什麼排序。
歸併排序
至於為什麼是歸併排序而不是什麼氣泡排序,快速排序……因為說的是(CDQ)分治嘛
至於為什麼不用平衡樹,樹狀陣列,權值線段樹等可以查詢排名的資料結構……後面套娃會用到
(這裡就不細講歸併排序的原理了)
程式碼:
#define mid ((l+r)>>1) void gbpx(int l,int r,int *a){//閉區間,a是那個亂七八糟沒排序的原始數列 if(l==r)return ; gbpx(l,mid,a);gbpx(mid+1,r,a); int t1=l,t2=mid+1; for(int i=l;i<=r;i++) if((a[t1]<a[t2] && t1<=mid) || t2>r)b[i]=a[t1++]; else b[i]=a[t2++]; for(int i=l;i<=r;i++) a[i]=b[i]; }
二維偏序
對於二維偏序,就一下子想到了類似的問題:求逆序對
求逆序對問題的兩個元素屬性分別是:位置和權值,求每個元素比
它位置小,且比它權值大的元素個數(和)
同理,只需要在程式碼上進行一些改動。
例題傳送:
P1908 逆序對
程式碼實現:
快讀不用也可以過,如果要用我(嫖水哥)的這個快讀的話,要開c++11.
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int a[N],b[N],n;
long long ans;
void CDQ(int l,int r,int *a){
if(l==r) return ;
int mid=(l+r)>>1;
CDQ(l,mid,a);CDQ(mid+1,r,a);
int t1=l,t2=mid+1;
for(int i=l;i<=r;i++)
if(a[t1]>a[t2] && t2<=r || t1>mid) b[i]=a[t2++],ans+=mid-t1+1;
else b[i]=a[t1++];
for(int i=l;i<=r;i++)a[i]=b[i];
}
namespace Read{
template<typename T>
inline void read(T &x){
x=0;T f=1;char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x*=f;
}
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
}
using namespace Read;
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
CDQ(1,n,a);
printf("%lld",ans);
return 0;
}
那麼話歸正題:
三維偏序
(題目傳送在上面)
程式碼實現:
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+105;
int n,k,cnt,d[N<<1],ans[N];
//結構體部分
struct node{int A,B,C,sz,ans;}a[N],b[N];
inline bool cmp1(node a,node b){
if(a.A!=b.A) return a.A<b.A;
if(a.B!=b.B) return a.B<b.B;
return a.C<b.C;
}//第一維排序
inline bool cmp2(node a,node b){return a.B<b.B;}
//樹狀陣列部分
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int v){for(;x<=k;x+=lowbit(x))d[x]+=v;}
inline int query(int x){
int res=0;
for(;x;x-=lowbit(x))res+=d[x];
return res;
}
//CDQ分治部分
void CDQ(int l,int r){
if(l==r) return ;
CDQ(l,mid);CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp2);sort(a+mid+1,a+r+1,cmp2);
//分割槽間排序
int t1=l,t2=mid+1;
for(int i=t2;i<=r;i++){
while(a[i].B>=a[t1].B && t1<=mid){add(a[t1].C,a[t1].sz);t1++;}
a[i].ans+=query(a[i].C);
}
for(int i=l;i<t1;i++)add(a[i].C,-a[i].sz);//要記得清空陣列
}
namespace Read{
template<typename T>
inline void read(T &x){
x=0;T f=1;char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
x*=f;
}
template <typename T, typename... Args>
inline void read(T& t, Args&... args) {
read(t); read(args...);
}
}
using namespace Read;
int main(){
read(n,k);
for(int i=1;i<=n;i++)read(b[i].A,b[i].B,b[i].C);
//合併同類項
sort(b+1,b+1+n,cmp1);
for(int i=1;i<=n;i++)
if(b[i].A!=b[i-1].A || b[i].B!=b[i-1].B || b[i].C!=b[i-1].C)a[++cnt]=(node){b[i].A,b[i].B,b[i].C,1};
else a[cnt].sz++;
//CDQ
CDQ(1,cnt);
for(int i=1;i<=cnt;i++)ans[a[i].ans+a[i].sz-1]+=a[i].sz;
for(int i=0;i<n;i++)printf("%d\n",ans[i]);
return 0;
}
座右銘:我從來沒有見過這樣陰鬱而又光明的日子。