日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

中石油训练赛 - Check List(线段树维护偏序问题)

發布時間:2024/4/11 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 中石油训练赛 - Check List(线段树维护偏序问题) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目大意:給出 n 個點,需要計算出滿足下列條件的三元對 ( i , j , k ) 的數量:

  • x[ i ]?< x[ j ]?< x[ k ]
  • y[ k?] > y[ i ] > y[ j?]
  • 題目分析:可以先對 x 進行排序,然后從左到右枚舉每一個點去計算其作為三元對中的第一個點、第二個點和第三個點時的貢獻

    先想一個簡單版本的問題,如果將偏序問題轉換為:

  • x[ i ]?< x[ j ]?< x[ k ]
  • y[ i ] <?y[ j?] <?y[ k?]
  • 這樣該如何去求解呢?這個模型相對就比較簡單了,可以直接用線段樹在 y 軸上維護一些變量:

  • sum1:有多少個點可以作為三元對中的第一個點
  • sum2:作為三元對中的第二個點,有多少個 二元點對 ,滿足 y[ i ] < y[ j ],將這個 點對 的位置記錄在位置 y[ j ] 上
  • 更新的話也比較簡單,因為所有點已經對 x 軸進行排序了,所以只需要按照如下順序更新即可,假設當前枚舉到的點為 ( x , y ):

  • 累加一下貢獻:區間 [ 1 , y - 1 ] 中的 sum2,因為已經有 sum2 個 二元點對 滿足 y[ i ] < y[ j ] 了,且滿足 y[ j ] < y,所以這些點對加上當前的點形成的三元對顯然滿足 y[ i ] < y[ j ] < y
  • 更新一下當前點作為第二個點時的貢獻:
  • 查詢一下區間 [ 1 , y - 1 ] 內有多少個 sum1,記為 val
  • 第 y 個位置的 sum2 加上 val
  • 顯然 val 個點都小于 y ,與 y 組成的二元點對滿足 sum2 的定義
  • 更新一下當前點作為第一個點時的貢獻:第 y 個位置的 sum1 加上 1
  • 時間復雜度是 nlogn 的,下面思考一下類比于上面的方法來解決本題

    仍然是用線段樹在?y 軸上維護一些變量:

  • sum1:有多少個點可以作為三元對中的第一個點
  • sum2:作為三元對中的第二個點,有多少個 二元點對,滿足 y[ i ] > y[ j ],將這個 點對 的位置記錄在位置 y[ i ] 上
  • 類比于上面的更新,設當前點為 ( x , y ):

  • 累加一下貢獻:區間 [ 1 , y - 1 ] 中的 sum2,因為已經有 sum2 個 二元點對 滿足 y[ i ] > y[ j ] 了,且滿足 y[ i?] < y,所以這些點對加上當前的點形成的三元對顯然滿足 y > y[ i ] > y[ j ]
  • 更新一下當前點作為第二個點時的貢獻:
  • 將區間 [ y + 1 , inf ] 內的所有 sum2?都加上 sum1
  • 因為我們需要將 y[ i ] > y 的這個二元對保存在 y[ i ] 的位置上,所以對于所有大于 y 的 y[ i ] 來說都需要執行加一的操作,也就是對應著上述的操作(有點繞,可以慢慢想想)
  • 更新一下當前點作為第一個點時的貢獻:第 y 個位置的 sum1 加上 1
  • 需要注意的點是:

  • 對于 x 軸相同的點需要統一處理
  • 需要對 y 軸上的點離散化一下建線段樹
  • 代碼:

    #pragma GCC optimize(2) #pragma GCC optimize("Ofast","inline","-ffast-math") #pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node;struct Point {int x,y;bool operator<(const Point& t)const{return x<t.x;} }p[N];struct Node {int l,r;LL sum1;//有多少個第一個點LL sum2;//有多少個第一個點與第二個點的匹配關系LL lazy;//sum2的下傳標記 }tree[N<<2];void pushup(int k) {tree[k].sum1=tree[k<<1].sum1+tree[k<<1|1].sum1;tree[k].sum2=tree[k<<1].sum2+tree[k<<1|1].sum2; }void pushdown(int k) {if(tree[k].lazy){LL lz=tree[k].lazy;tree[k].lazy=0;tree[k<<1].sum2+=tree[k<<1].sum1*lz;tree[k<<1|1].sum2+=tree[k<<1|1].sum1*lz;tree[k<<1].lazy+=lz;tree[k<<1|1].lazy+=lz;} }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;tree[k].sum1=tree[k].sum2=tree[k].lazy=0;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); }void update1(int k,int pos)//更新第一個點的貢獻 {if(tree[k].l==tree[k].r){tree[k].sum1++;return;}pushdown(k);int mid=tree[k].l+tree[k].r>>1;if(pos<=mid)update1(k<<1,pos);elseupdate1(k<<1|1,pos);pushup(k); }void update2(int k,int l,int r)//更新第一個點與第二個點的匹配貢獻 {if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].sum2+=tree[k].sum1;tree[k].lazy++;return;}pushdown(k);update2(k<<1,l,r);update2(k<<1|1,l,r);pushup(k); }LL query(int k,int l,int r)//返回第一個點和第二個點的匹配貢獻 {if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].sum2;pushdown(k);return query(k<<1,l,r)+query(k<<1|1,l,r); }void discreate() {sort(node.begin(),node.end());node.erase(unique(node.begin(),node.end()),node.end()); }int get_id(int x) {return lower_bound(node.begin(),node.end(),x)-node.begin()+1; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d",&p[i].x,&p[i].y);node.push_back(p[i].y);}sort(p+1,p+1+n);discreate();build(1,1,node.size());LL ans=0;for(int i=1,pos;i<=n;i++){pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新答案(作為第三個點的貢獻){int y=get_id(p[pos].y);ans+=query(1,1,y-1);pos++;}pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新第一個點和第二個點的匹配關系 {int y=get_id(p[pos].y);update2(1,y+1,node.size());pos++;}pos=i;while(pos<=n&&p[pos].x==p[i].x)//更新第一個點的貢獻 {int y=get_id(p[pos].y);update1(1,y);pos++;}i=pos-1;}printf("%lld\n",ans);return 0; }

    ?

    總結

    以上是生活随笔為你收集整理的中石油训练赛 - Check List(线段树维护偏序问题)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。